Compare commits

..

1840 Commits

Author SHA1 Message Date
96bccb3156 Doc update 2024-03-04 14:28:32 +02:00
Tal
234a4f352d Merge pull request #734 from Codium-ai/tr/fixes3
Update suggestion content in pr_code_suggestions_prompts.toml
2024-03-03 22:18:57 -08:00
eed23a7aaa Add truncation and summarization features to PR code suggestions 2024-03-04 08:16:05 +02:00
248c6b13be Update suggestion content in pr_code_suggestions_prompts.toml 2024-03-04 07:56:44 +02:00
Tal
7e07347b99 Merge pull request #733 from Codium-ai/tr/fixes3
readme updates
2024-03-03 05:10:08 -08:00
aa9dbf7111 updated readme 2024-03-03 15:04:59 +02:00
Tal
0709d5f663 Merge pull request #730 from Codium-ai/tr/issue_header
Enhance markdown formatting fo issues
2024-03-03 04:06:04 -08:00
c372c71514 prompt 2024-03-03 14:04:09 +02:00
b3fd05c465 try-except 2024-03-03 13:58:10 +02:00
Tal
68fab0bab9 Merge pull request #732 from Codium-ai/tr/remove_outdated_option
Remove functionality and references to 'remove_previous_review_comment'
2024-03-03 01:27:16 -08:00
f1bd67b7e9 Remove functionality and references to 'remove_previous_review_comment' option 2024-03-03 11:24:30 +02:00
Tal
5500d35856 Update Usage.md 2024-03-03 08:21:04 +02:00
5880221d00 Update logger setup to include debug level in github_app.py 2024-03-02 23:32:26 +02:00
Tal
c031653f68 Update README.md 2024-03-02 22:27:33 +02:00
Tal
5949a794c2 Update README.md 2024-03-02 22:26:24 +02:00
Tal
49dda61642 Update README.md 2024-03-02 22:25:55 +02:00
Tal
488caf2a90 Update Usage.md 2024-03-02 20:46:47 +02:00
Tal
9a0288250d Merge pull request #729 from Codium-ai/tr/wiki
wiki
2024-03-02 10:24:27 -08:00
917bdd5cb8 Refactor Usage.md for improved readability and organization 2024-03-02 20:23:10 +02:00
aabe96c7ff Refactor Usage.md for improved readability and organization 2024-03-02 20:20:08 +02:00
db796416d9 wiki 2024-03-02 20:16:29 +02:00
35315c070f major 2024-03-01 13:18:53 +02:00
7d081aa1d1 fix tests 2024-03-01 13:09:47 +02:00
e589dcb489 Enhance markdown formatting and update prompt descriptions in pr_reviewer_prompts.toml 2024-03-01 13:02:50 +02:00
Tal
60bd57538a Merge pull request #728 from Codium-ai/mrT23-patch-5
Update IMPROVE.md
2024-02-29 07:22:43 -08:00
Tal
a6cdd72966 Update IMPROVE.md 2024-02-29 17:20:15 +02:00
a86a3f52f0 a minor bugfix 2024-02-29 16:03:00 +02:00
Tal
2340f95488 Merge pull request #727 from Codium-ai/tr/repo_log
log event
2024-02-28 22:10:26 -08:00
dd4dc4b761 log event 2024-02-29 08:09:25 +02:00
Tal
6a51a646ee Merge pull request #725 from Codium-ai/tr/repo_log
get_log_context
2024-02-28 11:26:53 -08:00
8d498cd70c git_org 2024-02-28 16:14:13 +02:00
d5e72c2183 get_log_context 2024-02-28 16:10:57 +02:00
b09b936b15 get_log_context 2024-02-28 16:05:52 +02:00
Tal
6f22e5f557 Merge pull request #714 from Codium-ai/tr/improve_tweaks
improve tweaks
2024-02-28 05:57:38 -08:00
dbe772e708 capitalize 2024-02-28 13:59:04 +02:00
9c7ac125e1 Tweak analytics log message to add api_url 2024-02-28 12:05:01 +02:00
a8c5ac10b6 Merge remote-tracking branch 'origin/main' into tr/improve_tweaks 2024-02-28 11:09:48 +02:00
Tal
d54c5574a1 Merge pull request #723 from Codium-ai/tr/fixes2
some fixes
2024-02-27 23:27:54 -08:00
047c370683 Update README.md and add gfm markdown support check in pr_help_message.py 2024-02-28 09:20:14 +02:00
07f507c442 remove_initial_comment 2024-02-28 09:08:48 +02:00
39538c5356 cleaning 2024-02-28 09:01:39 +02:00
0c654b3b64 handle unsupported platforms for update changelog 2024-02-28 08:41:25 +02:00
Tal
92d3627de0 Update IMPROVE.md 2024-02-27 23:10:44 +02:00
4316d00941 log_context 2024-02-26 21:12:28 +02:00
edc9d8944e Refactor handle_closed_pr function to check for merged PRs 2024-02-26 20:56:43 +02:00
910c56c851 Merge pull request #721 from Codium-ai/ok/identity_provider
Identity provider
2024-02-26 20:23:09 +02:00
ab29cf2b30 Identity provider 2024-02-26 20:21:20 +02:00
Tal
540433b82c Merge pull request #720 from Codium-ai/tr/pr_statistics
Add functionality to calculate and log PR statistics on closure
2024-02-26 10:10:07 -08:00
60a37158b1 Add functionality to calculate and log PR statistics on closure 2024-02-26 20:09:01 +02:00
4921c26432 Add functionality to calculate and log PR statistics on closure 2024-02-26 20:02:11 +02:00
Tal
34fe2721fb Merge pull request #719 from Codium-ai/ok/identity_provider
Ok/identity provider
2024-02-26 09:34:06 -08:00
8bdc90c0f7 Identity provider 2024-02-26 19:00:21 +02:00
77831c793d Identity provider 2024-02-26 18:31:12 +02:00
Tal
5a2b5d97a0 Merge pull request #717 from Codium-ai/tr/logs_message
Add documentation for 'ignore_pr_titles' parameter in Usage.md
2024-02-26 06:43:22 -08:00
86c7c495f2 Add documentation for 'ignore_pr_titles' parameter in Usage.md 2024-02-26 16:29:40 +02:00
4b8fe84f88 Merge pull request #716 from Codium-ai/ok/analytics_support
Add analytics logging
2024-02-26 16:25:32 +02:00
8843f7bc8b Add analytics logging 2024-02-26 16:15:23 +02:00
Tal
64feef585a Merge pull request #715 from Codium-ai/tr/logs_message
small log improvement
2024-02-26 05:19:49 -08:00
ffe4512b7d small log improvement 2024-02-26 15:16:59 +02:00
a5cb35418e remove 'review_requested' (can trigger rate limit, and doesnt make sense algorighmically) 2024-02-26 14:20:50 +02:00
8594c93186 improve tweaks 2024-02-26 12:05:29 +02:00
Tal
116dd75a14 Merge pull request #712 from Codium-ai/tr/review_tweaks
Tr/review tweaks
2024-02-26 01:19:36 -08:00
85cdf05ca8 review formatting 2024-02-26 09:36:16 +02:00
7c9a389abf review formatting 2024-02-26 09:27:13 +02:00
18472492bc s 2024-02-26 09:14:12 +02:00
Tal
9002dccf6b Update IMPROVE.md 2024-02-25 19:10:56 +02:00
Tal
3ad53a34cd Update REVIEW.md 2024-02-25 17:33:24 +02:00
Tal
29714f9bd7 Merge pull request #710 from Codium-ai/tr/direct_link_bug
Refactor link generation in github_provider.py to use get_pr_url method
2024-02-25 07:28:47 -08:00
Tal
f921b5e9b9 Merge pull request #711 from Codium-ai/mrT23-patch-4
Update REVIEW.md
2024-02-25 07:23:04 -08:00
5f9969f30c base_url 2024-02-25 17:22:35 +02:00
Tal
0430f68d39 Update REVIEW.md 2024-02-25 17:17:55 +02:00
Tal
7f7045fd8a Update REVIEW.md 2024-02-25 17:16:10 +02:00
2dfddd8cea Fix a bug 2024-02-25 17:13:35 +02:00
bc88e0492f Fix a bug 2024-02-25 17:12:40 +02:00
a15d4f7a94 base_url 2024-02-25 16:55:20 +02:00
4258ce165b Refactor link generation in github_provider.py to use get_pr_url method 2024-02-25 16:33:19 +02:00
Tal
39e2b02933 Merge pull request #709 from Codium-ai/tr/adjustment
adjustment
2024-02-25 06:27:37 -08:00
1275cf0123 adjustment 2024-02-25 16:23:44 +02:00
5ab69af5a7 Merge pull request #695 from Codium-ai/ok/analytics
Add support for analytics file
2024-02-25 11:17:09 +02:00
Tal
118c9addaa Merge pull request #708 from Codium-ai/tr/logs
Refactor logging
2024-02-25 00:47:49 -08:00
dad3d3429f artifact 2024-02-25 10:45:15 +02:00
984a2888ae Refactor logging statements for better readability and debugging 2024-02-25 10:04:04 +02:00
8252b98bf5 Refactor logging statements for better readability and debugging 2024-02-25 10:01:53 +02:00
34e421f79b Refactor logging statements for better readability and debugging 2024-02-25 09:58:58 +02:00
877796b539 Refactor logging statements for better readability and debugging 2024-02-25 09:46:07 +02:00
Tal
df3a463668 Merge pull request #707 from KennyDizi/main
Introduced collapsible file list threshold as a constant
2024-02-24 06:29:44 -08:00
3bcf085f61 Fix context 2024-02-24 17:00:58 +07:00
f3a712683a Use constant in stead of hard code number for collapsible file list threadhold 2024-02-24 16:53:18 +07:00
Tal
51ce484bab Update github_app.py 2024-02-24 09:40:15 +02:00
Tal
214f65902c Merge pull request #701 from Codium-ai/ok/github_app_startup
Change github app startup logic to support gunicorj
2024-02-23 11:14:28 -08:00
4d8c38e5e1 Change github app startup logic to support gunicorj 2024-02-23 18:29:35 +02:00
2242f73661 Merge remote-tracking branch 'origin/ok/github_app_startup' into ok/github_app_startup 2024-02-23 18:29:09 +02:00
2f3171e422 Change github app startup logic to support gunicorj 2024-02-23 18:28:58 +02:00
90599f53d4 Change github app startup logic to support gunicorj 2024-02-23 18:27:34 +02:00
b878f64793 add analytics support 2024-02-23 17:30:20 +02:00
4242e157ab Merge remote-tracking branch 'origin/main' into ok/analytics 2024-02-23 17:16:43 +02:00
Tal
6df2a6769e Update DESCRIBE.md 2024-02-22 22:03:39 +02:00
Tal
dadb760aae Merge pull request #696 from Codium-ai/tr/app_refactor
Refactor GitHup App
2024-02-22 09:28:44 -08:00
85492f20fa 'debug' for request body 2024-02-22 18:13:42 +02:00
8b76eb1014 fixed bugs with incremental review 2024-02-22 18:03:00 +02:00
adc5709b29 Refactor github_app.py to improve handling of PR events and comments 2024-02-22 17:34:51 +02:00
b884920ef2 Refactor github_app.py to improve handling of PR events and comments 2024-02-22 17:26:47 +02:00
4ebac16ff7 Add support for analytics file 2024-02-22 13:21:56 +02:00
ee59d34e39 Merge pull request #694 from Codium-ai/ok/title_regex
Adds an option to ignore PR opens by regex matching
2024-02-22 12:26:57 +02:00
e3dba12fea Adds an option to ignore PR opens by regex matching 2024-02-22 12:14:04 +02:00
Tal
42bcda1eb8 Merge pull request #687 from Codium-ai/tr/ado
Update README.md to include Azure DevOps in supported platforms
2024-02-21 11:10:51 -08:00
1b214114ec Update README.md to include Azure DevOps in supported platforms 2024-02-21 21:06:47 +02:00
Tal
764aa8d679 Merge pull request #686 from Codium-ai/tr/reduce_number_of_calls
Tr/reduce number of calls
2024-02-21 08:26:10 -08:00
4d0f691b64 repo_settings 2024-02-21 18:23:34 +02:00
048d90623f Ignore comment not starting with a slash 2024-02-21 17:59:58 +02:00
Tal
7c87c9d3a5 Merge pull request #685 from Codium-ai/tr/reduce_number_of_calls
repo_settings
2024-02-21 07:20:21 -08:00
8ffdaf00c1 repo_settings 2024-02-21 17:18:54 +02:00
Tal
a94b5c6682 Merge pull request #684 from Codium-ai/tr/reduce_number_of_calls
Tr/reduce number of calls
2024-02-21 07:05:00 -08:00
fc7b267c9a self.diff_files 2024-02-21 17:00:11 +02:00
e291bd352e protections 2024-02-21 16:46:57 +02:00
f08ce53de3 Optimize PR commit retrieval and caching in GitHub provider and utils 2024-02-21 16:33:32 +02:00
Tal
34797fe809 Merge pull request #683 from Codium-ai/tr/fixes
Tr/fixes
2024-02-21 01:11:40 -08:00
f3c1c61c2e readme 2024-02-21 11:11:03 +02:00
0f1614bedc readme 2024-02-21 11:10:03 +02:00
a41a427b58 readme 2024-02-21 11:08:08 +02:00
b1dfd905c4 text 2024-02-21 09:40:39 +02:00
dd5386e07e try-except 2024-02-21 09:27:40 +02:00
275f0d6a05 Update GitLab configuration and documentation for webhook setup 2024-02-21 09:20:28 +02:00
0e3417b4ab webhook 2024-02-21 08:55:59 +02:00
Tal
d0a86ab684 Merge pull request #682 from jfouchard/patch-2
Add a note about change the target for Gitlab install
2024-02-20 06:58:21 -08:00
1348a67cd2 Add more detail to the pr-agent url 2024-02-20 08:33:34 -05:00
7b98db20d5 Add a note about change the target for Gitlab install
This is just a minor documentation update about changing the target when building the Docker image for Gitlab.  While it's obvious in retrospect, if you jump straight to the Gitlab section of the document how this is supposed to work.  If you follow the directions exactly you run into [this issue](https://github.com/Codium-ai/pr-agent/issues/456)
2024-02-20 08:24:43 -05:00
Tal
a4467a5773 Merge pull request #679 from Codium-ai/tr/fixes
Refactor reaction handling in GitHub provider and update help text
2024-02-19 22:11:48 -08:00
4a0b12c036 Refactor reaction handling in GitHub provider and update help text in PR tools 2024-02-20 08:06:33 +02:00
Tal
6eca495801 Merge pull request #666 from yochail/yochail/support_azure_devops_managed_identity
Add Az Devops managed identity support
2024-02-19 21:44:23 -08:00
Tal
5c8160444a Merge pull request #678 from Codium-ai/tr/help_invoke
Tr/help invoke
2024-02-19 11:48:13 -08:00
82ba285395 finalize 2024-02-19 21:44:00 +02:00
2be0339a27 help improved 2024-02-19 21:42:13 +02:00
Tal
3e7f83ffdb Merge pull request #677 from Codium-ai/tr/help_invoke
help improved
2024-02-19 11:21:31 -08:00
8d6c6a35db help improved 2024-02-19 21:10:20 +02:00
34e89e45bd help improved 2024-02-19 21:07:24 +02:00
Tal
c3ff5c46a6 Merge pull request #676 from Codium-ai/tr/bitbucket_review_suggestions
bitbucket code suggestions
2024-02-19 09:55:14 -08:00
345f923569 bitbucket code suggestions 2024-02-19 19:52:49 +02:00
0f815876e5 bitbucket code suggestions 2024-02-19 19:46:57 +02:00
d47a840179 bitbucket code suggestions 2024-02-19 19:43:31 +02:00
3770704db7 reset commit 2024-02-19 08:35:45 -05:00
Tal
9cc147dda0 Merge pull request #675 from Codium-ai/hl/notification_readme
Update Usage.md
2024-02-19 04:10:34 -08:00
787b82d888 Update Usage.md 2024-02-19 14:07:46 +02:00
Tal
20483d63b7 Merge pull request #674 from Codium-ai/tr/help_command
Refactor PR help message and update related documentation
2024-02-18 22:35:32 -08:00
36aa22bd18 Refactor PR help message and update related documentation 2024-02-19 08:30:45 +02:00
Tal
d9775e6b8c Merge pull request #673 from Codium-ai/tr/help_command
Added PRHelpMessage to command execution in pr_agent.py
2024-02-18 03:16:36 -08:00
28e8707c1b Added PRHelpMessage to command execution in pr_agent.py 2024-02-18 13:16:07 +02:00
687ece1e86 Added PRHelpMessage to command execution in pr_agent.py 2024-02-18 13:09:17 +02:00
0515b80247 Added PRHelpMessage to command execution in pr_agent.py 2024-02-18 13:08:05 +02:00
ed5856493c Added PRHelpMessage to command execution in pr_agent.py 2024-02-18 13:06:57 +02:00
e9382b18b6 Added PRHelpMessage to command execution in pr_agent.py 2024-02-18 12:01:16 +02:00
Tal
a70e5c9e1f Update README.md 2024-02-18 09:57:34 +02:00
Tal
9991fde864 Merge pull request #669 from Codium-ai/tr/readme_18_2
readme
2024-02-17 22:27:51 -08:00
163132dd6d quotes 2024-02-18 08:25:30 +02:00
8b27559510 table 2024-02-18 08:22:59 +02:00
688cb374f6 lint 2024-02-18 08:19:01 +02:00
e821ba2a5b readme 2024-02-18 08:16:47 +02:00
d371bff0ba readme 2024-02-18 08:14:21 +02:00
Tal
7b15101051 Merge pull request #661 from Codium-ai/hl/ask_line
Hl/ask line
2024-02-17 22:08:55 -08:00
5918943959 readme 2024-02-18 08:07:12 +02:00
Tal
cd8a40c7a6 Merge pull request #665 from Codium-ai/tr/checks_readme
Update README and documentation with new CI Feedback tool
2024-02-17 22:04:59 -08:00
Tal
2b12042a85 Merge pull request #667 from Codium-ai/tr_ado
azure webhook
2024-02-17 22:01:57 -08:00
9e3b79b21b readme 2024-02-18 07:59:53 +02:00
c6cb0524b4 rstrip 2024-02-18 07:56:14 +02:00
481a4fe7a1 revert 2024-02-17 19:43:34 +02:00
de4af313ba azure dev ops 2024-02-17 19:40:06 +02:00
b402bd5591 revert azuredevops_provider.py change 2024-02-17 08:36:26 -05:00
cb3ebd9169 Update README and documentation with new CI Feedback tool 2024-02-16 20:40:45 +02:00
c98e736e3b added github action support 2024-02-16 14:49:01 +02:00
40fbd55da4 added github action support 2024-02-16 12:58:55 +02:00
3eef0a4ebd fix line selection, don't support line deletions 2024-02-15 22:21:58 +02:00
6712c0a7f8 remove unnecessary call 2024-02-15 21:43:25 +02:00
cfe794947d Gitlab /ask line works 2024-02-15 21:35:51 +02:00
24dd57e5b7 clean 2024-02-15 17:14:06 +02:00
8ed98c8a4f Add Documentation 2024-02-15 15:20:15 +02:00
fff52e9e26 Add ask line feature 2024-02-15 14:25:22 +02:00
4947c6b841 Merge pull request #660 from Codium-ai/ok/add_command_to_log
Add logging context to command execution in pr_agent.py
2024-02-15 12:27:25 +02:00
433b8d24b8 Add logging context to command execution in pr_agent.py 2024-02-15 12:13:56 +02:00
bd88c66717 Merge remote-tracking branch 'origin/main' 2024-02-15 08:45:25 +02:00
d2ad8b1dbd Refactor publish_persistent_comment method to include name parameter 2024-02-15 08:45:17 +02:00
1053fa84f6 rename azure_devops_server var 2024-02-13 22:27:07 -05:00
b833d63468 PR comment: change name to azure_devops_server 2024-02-13 22:25:52 -05:00
Tal
70b83bac78 Merge pull request #659 from Codium-ai/tr/bitbucket_review
no html bitbucket
2024-02-13 08:40:57 -08:00
54a989d30f no html bitbucket 2024-02-13 18:37:48 +02:00
480a890741 no html bitbucket 2024-02-13 18:33:22 +02:00
2f327c26e8 auto approve 2024-02-13 11:21:59 +02:00
9ff62dce08 Add legacy url support 2024-02-12 18:40:06 -05:00
e8c2ec034d Update azuredevops_server_webhook.py
fix returned HTTP status
2024-02-12 18:38:08 -05:00
bbd0d62c85 fix auto_describe key 2024-02-11 18:10:22 -05:00
8fa058ff7f add azure devops pat to secret template config 2024-02-11 18:06:56 -05:00
34378384da add get endpoint for container status 2024-02-11 17:59:02 -05:00
95344c7083 fix basic auth 2024-02-11 17:42:06 -05:00
bc38fad4db add support for auto events 2024-02-11 17:23:56 -05:00
076d8e7187 fix PR code suggestions 2024-02-11 17:17:25 -05:00
22d0c275d7 fix PR comments 2024-02-11 17:13:59 -05:00
a168defd28 clean readme 2024-02-11 17:09:09 -05:00
b7a522ed69 add docker file 2024-02-11 17:05:44 -05:00
86d4a31eef add docs 2024-02-11 17:02:14 -05:00
9a54be5414 add webhook support 2024-02-11 16:52:49 -05:00
Tal
d0958022a0 Merge pull request #649 from rajyan/patch-2
filter events to align with pr-agent's setting
2024-02-11 10:54:42 -08:00
Tal
ec2aab805d Merge pull request #650 from yochail/yocail/support_azure_inline_comment
Support Azure Inline Comment
2024-02-11 10:54:10 -08:00
47060ddcac fix PR comments
- added line position
- added try-catch per comment
2024-02-11 12:40:36 -05:00
Tal
60d6fecd37 Merge pull request #653 from Codium-ai/hl/loading_improve
add loading comment to /improve
2024-02-11 07:28:57 -08:00
Tal
bdbb101183 Merge pull request #655 from Codium-ai/ok/disclaimer
Add AI disclaimer fields to configuration.toml
2024-02-11 07:28:47 -08:00
8a677e07a2 Fix a typo 2024-02-11 16:39:38 +02:00
3f42bb6793 Add AI disclaimer fields to configuration.toml 2024-02-11 16:22:26 +02:00
3420c6cf79 update label to https://github.com/Codium-ai/pr-agent/pull/654 2024-02-11 23:21:58 +09:00
Tal
159e2f7dd6 Merge pull request #654 from rajyan/rajyan-patch-2
add pull_request event triggers for github action
2024-02-11 06:08:48 -08:00
67fde2c17e add pull_request event triggers for github action
https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
2024-02-11 21:21:20 +09:00
0e08520c0c match pr-pro 2024-02-11 13:21:59 +02:00
6c500413f1 default behavior for bitbucket 2024-02-11 13:14:47 +02:00
73d2b1565d Implement edit comment 2024-02-11 12:31:30 +02:00
a40643bbba add return response 2024-02-11 12:20:06 +02:00
d93a24bbf7 add loading comment to /improve 2024-02-11 12:14:25 +02:00
Tal
79dac3f419 Merge pull request #651 from Codium-ai/tr/br_inside_code
insert_br_after_x_chars can already handle code
2024-02-11 01:47:12 -08:00
c75413fac5 count_chars_without_html 2024-02-11 11:37:11 +02:00
4e386153ea insert_br_after_x_chars can already handle code 2024-02-11 11:32:16 +02:00
8800bad658 revamped 2024-02-11 11:00:12 +02:00
cfb576d1ae revamped 2024-02-11 10:58:13 +02:00
7c9b65ba65 prompts 2024-02-11 08:05:09 +02:00
ba854c228b Update azuredevops_provider.py 2024-02-10 22:36:01 -05:00
d8ea2731ea add support for azure inline commnets 2024-02-11 03:27:47 +00:00
078e87139a possible_issues 2024-02-09 21:34:07 +02:00
bf11033349 possible_issues 2024-02-09 21:27:22 +02:00
01fbebfc5e relevant tests 2024-02-09 12:50:51 +02:00
Tal
b660badd09 Merge pull request #646 from Codium-ai/hl/pr_review_table
Hl/pr review table
2024-02-09 01:49:59 -08:00
796e203c01 rstrip() 2024-02-09 11:45:12 +02:00
6837e43114 help 2024-02-09 11:30:28 +02:00
555151602f rstrip() 2024-02-09 11:26:43 +02:00
f74b35fb6f Merge remote-tracking branch 'origin/main' into hl/pr_review_table 2024-02-09 11:05:13 +02:00
f8e1bd3d4c get_pr_url 2024-02-09 11:02:23 +02:00
e01025706a filter events to align with pr-agent's setting 2024-02-09 17:45:29 +09:00
5af9e8e749 fix 2024-02-08 23:53:29 +02:00
24c575737c fix2 2024-02-08 23:05:56 +02:00
4175e8c467 fix test2 2024-02-08 20:49:42 +02:00
77e7463395 fix tests 2024-02-08 20:14:25 +02:00
2e1462580f s 2024-02-08 19:02:56 +02:00
fa077dc516 formatting 2024-02-08 18:55:58 +02:00
a3f4c44632 PR Review 2024-02-08 15:25:43 +02:00
4447110118 small fix 2024-02-08 14:35:39 +02:00
c2088b7752 Merge commit 'e4f177908b620e46740b03966fda9243473d979e' into hl/pr_review_table 2024-02-08 14:26:29 +02:00
ddb89a7474 New PR Reviewer with Table view 2024-02-08 14:26:14 +02:00
Tal
e4f177908b Merge pull request #644 from Codium-ai/tr/parallel_calls
Tr/parallel calls
2024-02-07 10:29:00 -08:00
b077873c3d parallel_calls 2024-02-07 08:00:16 +02:00
a7ce2b11b4 parallel_calls 2024-02-07 08:00:01 +02:00
Tal
ef1b0ce3e3 Merge pull request #643 from Codium-ai/hl/docs_add_table_trigger
DOCS: add table to files changes action
2024-02-06 21:32:25 -08:00
Tal
ea3a34be08 Update DESCRIBE.md 2024-02-07 07:31:21 +02:00
9a26ed7c43 Update DESCRIBE.md 2024-02-06 21:19:47 +02:00
5d81f31ccc Update DESCRIBE.md 2024-02-06 21:18:56 +02:00
Tal
92d2f484d2 Merge pull request #642 from Codium-ai/tr/readme
readme cleaning
2024-02-06 08:04:55 -08:00
91e77787c0 readme cleaning 2024-02-06 18:03:14 +02:00
Tal
034c654bac Update .pr_agent.toml 2024-02-06 17:47:14 +02:00
Tal
94e2f00c06 Merge pull request #641 from Codium-ai/tr/auto_approve
auto approval feature
2024-02-06 07:18:43 -08:00
2bc398f74e readme 2024-02-06 16:48:29 +02:00
1c9bd3e9a8 get_pr_url 2024-02-06 09:26:00 +02:00
8a04a4f481 auto approval 2024-02-06 09:12:52 +02:00
3e96812c5d Merge remote-tracking branch 'origin/main' into tr/auto_approve 2024-02-06 09:10:00 +02:00
b190b1879e auto approval 2024-02-06 09:09:07 +02:00
Tal
9d3e6620c1 Merge pull request #640 from Codium-ai/tr/publish_output
publish_output fix
2024-02-05 21:47:23 -08:00
Tal
4636c8a1fa Merge pull request #634 from Codium-ai/tr/secret_provider
protections for 'get_secret_provider'
2024-02-05 21:46:17 -08:00
3773303bfd publish_output fix 2024-02-06 07:44:20 +02:00
Tal
a126ef64fc Merge pull request #639 from Codium-ai/tr/describe_bullets
Tr/describe bullets
2024-02-05 05:55:25 -08:00
c1c7b3b6da fixed code 2024-02-05 13:00:57 +02:00
2b6e8c3f09 minor change 2024-02-05 12:39:03 +02:00
Tal
d4e78118fe Merge pull request #638 from Codium-ai/tr/describe_bullets
insert_br_after_x_chars
2024-02-05 02:06:13 -08:00
cce3c70369 - patch_extra_lines = 1
- describe is with turbo model (for larger context)
2024-02-05 12:03:30 +02:00
32e8ba331a insert_br_after_x_chars 2024-02-05 10:12:47 +02:00
3f2a7869dd insert_br_after_x_chars 2024-02-05 09:22:26 +02:00
2ee329674f insert_br_after_x_chars 2024-02-05 09:20:36 +02:00
e104bd7a3f large patch protection 2024-02-04 16:27:57 +02:00
3e128869dc large patch protection 2024-02-04 16:10:53 +02:00
cf2ed9d483 Merge remote-tracking branch 'origin/main' 2024-02-04 14:25:57 +02:00
e1b0e4a40a minor prompt changes 2024-02-04 14:24:55 +02:00
Tal
1013f7586b Merge pull request #637 from Codium-ai/tr/pr-actions
pr-actions
2024-02-02 04:21:33 -08:00
023f2eb77c pr-actions 2024-02-02 14:17:45 +02:00
Tal
cb8ff2b318 Merge pull request #636 from Codium-ai/tr/model_turbo
moving the 'improve' command to turbo mode, with auto_extended=true
2024-02-01 06:57:42 -08:00
d04d8b616a moving the 'improve' command to turbo mode, with auto_extended=true 2024-02-01 09:46:04 +02:00
Tal
2816cd2c4b Update README.md 2024-02-01 09:06:01 +02:00
2112defa51 lancedb bump 2024-02-01 08:44:23 +02:00
9579be028d protections for 'get_secret_provider' 2024-02-01 08:31:11 +02:00
Tal
7168326911 Update TOOLS_GUIDE.md 2024-01-30 08:22:29 +02:00
Tal
e1ae51e7a0 Update TOOLS_GUIDE.md 2024-01-30 08:21:43 +02:00
Tal
c69962479a Merge pull request #630 from Codium-ai/tr/language
Enhancements in Patch Formatting and Code Suggestions Handling
2024-01-29 12:11:23 -08:00
15c8fe94bb feat: Improve patch formatting and handle empty data in pr_code_suggestions.py 2024-01-29 22:00:11 +02:00
0d86779799 feat: Improve patch formatting and handle empty data in pr_code_suggestions.py 2024-01-29 21:52:54 +02:00
6565556e01 feat: Add 'language' field to CodeSuggestion, FileDescription, and ReviewerPrompt models in settings files 2024-01-29 20:51:24 +02:00
Tal
6998089549 Update README.md 2024-01-29 20:21:23 +02:00
8d36e2e2f7 feat: Add new configuration options in pr_test section and update TEST.md documentation 2024-01-29 20:17:39 +02:00
Tal
93f1854c68 Merge pull request #629 from Codium-ai/tr/tests
s
2024-01-29 01:43:46 -08:00
40a7ef9132 s 2024-01-29 11:42:32 +02:00
042eab1641 s 2024-01-29 11:39:50 +02:00
567b400f97 Revert "s1"
This reverts commit 412159bba5.
2024-01-29 11:30:58 +02:00
412159bba5 s1 2024-01-29 11:28:58 +02:00
Tal
e6f548920b Merge branch 'main' into tr/tests 2024-01-29 01:23:27 -08:00
f1fe2563f4 s 2024-01-29 11:22:46 +02:00
467e2ae68e s 2024-01-29 11:19:37 +02:00
78bb54bd8f s 2024-01-29 11:11:30 +02:00
Tal
2bebfba4b6 Update README.md 2024-01-28 20:39:45 +02:00
Tal
815073e04f Merge pull request #628 from Codium-ai/tr/tests
tests readme
2024-01-28 10:32:27 -08:00
5f1722ed4a s 2024-01-28 20:30:40 +02:00
47af04d158 s 2024-01-28 20:26:58 +02:00
Tal
335654b02a Update Usage.md 2024-01-27 21:38:25 +02:00
Tal
68e17ed2be Merge pull request #627 from Codium-ai/tr/updates2
Tr/updates2
2024-01-27 11:33:33 -08:00
ecb46435b3 s 2024-01-27 21:29:19 +02:00
98ce0a7036 s 2024-01-27 21:25:43 +02:00
76f44b13f8 docs: Update GitHub app configurations section in Usage.md 2024-01-27 21:20:10 +02:00
06dede29f2 feat: Update configuration and handling of GitHub Action settings 2024-01-27 21:15:23 +02:00
Tal
dbf5ebcb8d Merge pull request #622 from eltociear/fix-filename
docs: fix file name
2024-01-25 02:09:27 -08:00
Tal
d6a45663f1 Merge pull request #624 from Codium-ai/hl/small_fixes
small fixes
2024-01-25 02:09:04 -08:00
07eaa59e78 small fixes 2024-01-25 11:07:43 +02:00
1e2d4e9830 docs: fix file name 2024-01-25 15:03:58 +09:00
Tal
cc03f7f615 Merge pull request #620 from Codium-ai/tr/updates
Configuration updates
2024-01-24 09:59:43 -08:00
Tal
040da2fbb1 Merge pull request #612 from Codium-ai/mrT23-patch-1
Update README.md
2024-01-24 09:59:25 -08:00
Tal
a83a492b22 Merge branch 'main' into mrT23-patch-1 2024-01-24 09:58:23 -08:00
e056cd5988 type 2024-01-24 19:55:33 +02:00
4077c5556d enable_review_labels_effort set to true by default 2024-01-24 19:49:43 +02:00
d8465ea9f9 removed include_improved_code 2024-01-24 19:47:30 +02:00
f4037e0dfa feat: Add LanceDB support for similar_issue tool and refactor SOC2 compliance feature name 2024-01-24 19:40:58 +02:00
9986f5307c Merge pull request #618 from Codium-ai/hl/describe_usage_guide
Enhance Documentation for "Inline File Walkthrough" Feature
2024-01-24 10:15:21 +02:00
60c0371854 highlight options 2024-01-23 18:13:08 +02:00
139bbfc67a update docs and usage guide 2024-01-23 17:58:55 +02:00
b33b8c12cd Merge pull request #616 from Codium-ai/hl/walkthrough_title_ui_improvements
update default config for inline_file_summary to false
2024-01-22 10:37:02 +02:00
968684b461 update default config for inline_file_summary to false 2024-01-22 10:25:34 +02:00
Tal
4a5cff4995 Update CUSTOM_SUGGESTIONS.md 2024-01-21 17:58:01 +02:00
599c6773f3 Merge pull request #613 from Codium-ai/hl/walkthrough_title_ui_improvements
Add changes title of files and improve table style and alignments
2024-01-21 17:52:44 +02:00
Tal
7178ddac10 Update CUSTOM_SUGGESTIONS.md 2024-01-21 17:31:33 +02:00
Tal
5dedc381a6 Merge pull request #615 from Codium-ai/mrT23-patch-2
Update README.md
2024-01-21 07:30:14 -08:00
Tal
cba14ada2c Update README.md 2024-01-21 17:29:29 +02:00
Tal
f81fe0a12d Merge pull request #614 from Codium-ai/tr/custom_suggestions
feat: Add custom suggestions tool to README.md
2024-01-21 07:19:22 -08:00
78d886459a feat: Add custom suggestions tool to README.md 2024-01-21 17:15:34 +02:00
27aafb06cb feat: Add custom suggestions tool to README.md 2024-01-21 17:10:23 +02:00
329f7fa9d6 feat: Add custom suggestions tool to README.md 2024-01-21 17:06:25 +02:00
e79919b5c6 update describe screenshot to the new describe 2024-01-21 14:09:17 +02:00
8d513e078a Add changes title of files and improve table style and alignments 2024-01-21 13:43:37 +02:00
Tal
69f7923552 Update README.md 2024-01-20 13:02:07 +02:00
Tal
2430a1a608 Merge pull request #594 from Codium-ai/tr/fallback_bad_review_comment
Enhanced Comment Verification and Fallback Mechanism for Inline Comment Publishing
2024-01-20 02:04:06 -08:00
e54388d807 s 2024-01-20 11:59:45 +02:00
d942bdb8bd s 2024-01-20 11:56:17 +02:00
Tal
84d87aa870 Merge pull request #607 from Codium-ai/tr/edge_cases
feat: Improve error handling and code readability in pr_agent tools
2024-01-18 07:09:07 -08:00
39891e4ab1 feat: Improve error handling and code readability in pr_agent tools 2024-01-18 17:01:25 +02:00
d7858efbbe Merge pull request #581 from Codium-ai/sm/azure_devops
Enhancement of AzureDevopsProvider with new functionalities and refactoring
2024-01-18 16:28:28 +02:00
Tal
b3365b8d6c Merge pull request #605 from Codium-ai/tr/edge_cases
No suggestions found
2024-01-18 06:18:43 -08:00
fc5b00f4d3 s 2024-01-18 16:11:44 +02:00
Tal
5150e66723 Merge pull request #603 from Codium-ai/mrT23-patch-1
Update README.md
2024-01-17 22:21:44 -08:00
Tal
4dad1af77b Update README.md 2024-01-18 08:20:09 +02:00
Tal
02129b40cf Merge pull request #601 from Codium-ai/hl/diffview_file_summary
Readme Inline file summary 💎
2024-01-17 06:37:53 -08:00
3fb6d17338 width 2024-01-17 16:36:26 +02:00
3be7bfce79 feat: Add repository labels retrieval function in gitlab_provider.py
docs: Update links and add Inline file summary to TOC in DESCRIBE.md
2024-01-17 16:33:48 +02:00
472646ddfd Readme 2024-01-17 16:27:07 +02:00
eb4a1c515e Merge pull request #600 from Codium-ai/tr/improve_usage_guide
readme updates
2024-01-17 15:55:42 +02:00
e4af0b22ad s 2024-01-17 15:51:42 +02:00
a3e59a418e Merge remote-tracking branch 'origin/main' into tr/improve_usage_guide 2024-01-17 15:46:05 +02:00
4e833c0c28 s 2024-01-17 15:43:01 +02:00
Tal
0b811d97a7 Merge pull request #598 from Codium-ai/tr/improve_usage_guide
Enhancements to the 'improve' tool and updates to the related documentation
2024-01-17 03:13:33 -08:00
8f510dc553 s 2024-01-17 11:47:59 +02:00
2132771f46 s 2024-01-17 11:29:50 +02:00
e66bd7caa7 fallback to commitable 2024-01-17 11:18:30 +02:00
17ce2f0ed0 improve usage guide 2024-01-17 10:09:44 +02:00
7298548f82 improve usage guide 2024-01-17 10:06:27 +02:00
298c41a100 improve usage guide 2024-01-17 10:03:48 +02:00
58163e5129 improve usage guide 2024-01-17 09:50:48 +02:00
Tal
fae3bf6309 Merge pull request #590 from EduardDurech/patch-2
Fixed Run from source instructions for Python
2024-01-16 22:53:36 -08:00
06f0235577 Merge pull request #597 from Codium-ai/hl/improve_ui_table
Hl/improve UI table
2024-01-16 09:46:34 +02:00
d7e0aad527 small fixes 2024-01-16 09:41:31 +02:00
31576b77ff improve backticks 2024-01-15 19:07:41 +02:00
ea39e8684f works 2024-01-15 16:42:50 +02:00
afefc15b9c improve doce suggestions UI with difflib 2024-01-15 15:56:48 +02:00
5e17ccaf86 add colaplsable 2024-01-15 15:17:57 +02:00
9b1eb86d75 first iteration of improved UI for /improve --extended 2024-01-15 15:10:54 +02:00
9f5c2e5f17 feat: Refactor comment verification in github_provider.py 2024-01-14 11:55:07 +02:00
7377f4e4b2 feat: Refactor comment verification in github_provider.py 2024-01-14 11:49:51 +02:00
d6f4c1638d feat: Refactor comment verification in github_provider.py 2024-01-14 10:49:05 +02:00
a58c385b0f Fixed Rust warning tip as behaviour is inconsistent 2024-01-14 04:16:32 +01:00
7a3830d228 Fixed Run from source instructions for Python
Previously only installed dependencies but not pr_agent

+ Fixed link to OpenAI API Key and added for GitHub access token
2024-01-13 06:29:07 +01:00
Tal
e7251ada3f Merge pull request #588 from barnett-yuxiang/ignore-ds-store
Ignore .DS_Store files
2024-01-10 07:25:49 -08:00
aca3fcb571 Ignore .DS_Store files
This commit updates the .gitignore file to ignore .DS_Store files, which are created by macOS. These files are not relevant to the project and should not be included in version control.

Signed-off-by: Kamakura.Yx <barnett.yuxiang@gmail.com>
2024-01-10 23:13:57 +08:00
b9951fce62 Typo when parsing the suggestion part 2024-01-10 11:59:45 +02:00
Tal
609836bd6a Merge pull request #587 from Codium-ai/tr/abbrevations
Enhancement: Improve PR-Agent Tool with Custom Labels, Extra Instructions, and Summarize Mode
2024-01-09 23:39:04 -08:00
09ee0b64ba feat: Refactor instructions and fields in pr_code_suggestions_prompts.toml 2024-01-10 09:37:05 +02:00
Tal
fb4746fd09 Merge pull request #584 from samanhappy/link
Fix link formatting for relevant_line
2024-01-09 23:22:55 -08:00
729b5d11c9 feat: Refactor instructions and fields in pr_code_suggestions_prompts.toml 2024-01-09 22:56:25 +02:00
fc502a6fd5 feat: Refactor instructions and fields in pr_code_suggestions_prompts.toml 2024-01-09 22:49:26 +02:00
2b607dbd9a feat: Refactor instructions and fields in pr_code_suggestions_prompts.toml 2024-01-09 22:32:09 +02:00
9c6aabb0bb feat: Add custom labels and extra instructions sections to help.py, summarize mode to pr_code_suggestions.py, and summarize mode condition to pr_code_suggestions_prompts.toml 2024-01-09 22:09:48 +02:00
Tal
da3ac656ee Merge pull request #586 from Codium-ai/tr/ask_usage
ask helper
2024-01-09 06:55:30 -08:00
a42e57d09b ask helper 2024-01-09 16:36:39 +02:00
e56c443fd6 ask helper 2024-01-09 16:34:27 +02:00
abc05e7711 ask helper 2024-01-09 16:25:23 +02:00
Tal
a77d539866 Merge pull request #585 from samanhappy/url
Enhancement: Update GitLab link generation to support self-managed GitLab server and different projects
2024-01-09 06:12:05 -08:00
19c14b940e Try fixing invalid inline comments 2024-01-09 09:54:29 +02:00
36f1cfb51f Enhancement: Update GitLab link generation to support self-managed GitLab server and different projects 2024-01-09 15:11:27 +08:00
0f2a4654a7 Fix link formatting for relevant_line 2024-01-09 14:56:18 +08:00
28c5ad1d8b nit 2024-01-08 13:06:03 +02:00
2bb5ae8c0d Remove redundant condition (status 422 already means the same) 2024-01-08 13:05:10 +02:00
b0bffdec84 Refactor and add configuration toggle 2024-01-08 12:00:20 +02:00
Tal
11b96b1c1a Merge pull request #583 from Codium-ai/tr/unique_titles
feat: Remove bot help text from github_polling.py
2024-01-08 01:51:43 -08:00
e0f4bc7ded feat: Remove bot help text from github_polling.py 2024-01-08 11:45:01 +02:00
Tal
62d83f6753 Merge pull request #582 from Codium-ai/tr/unique_titles
Enhance PR description headers with bold formatting and include original user description
2024-01-08 01:28:56 -08:00
e9a2a0a96f s 2024-01-08 10:37:51 +02:00
46a38473e4 Merge remote-tracking branch 'origin/main' into tr/unique_titles 2024-01-08 10:30:58 +02:00
c9e55be275 s 2024-01-08 10:30:47 +02:00
Tal
f714592dec Merge pull request #579 from Codium-ai/tr/user_description
Enhancements to Logging, Help Messages, and PR Descriptions and Reviews
2024-01-07 23:46:25 -08:00
8bb2eb48af s 2024-01-08 09:43:34 +02:00
9cfb8ce475 s 2024-01-08 09:39:19 +02:00
67cb133c52 s 2024-01-08 09:28:44 +02:00
9c054bb80f s 2024-01-08 09:18:46 +02:00
b776e5069c feat: Refactor AzureDevopsProvider class in azuredevops_provider.py
- Reorder class methods and constructor for better readability
- Add error logging for failed operations
- Implement get_pr_description_full method
- Update get_pr_description method to always return full description
- Modify _parse_pr_url method to return workspace_slug, repo_slug, and pr_number
- Make _get_azure_devops_client a static method
- Add error handling in get_pr_id method
2024-01-08 09:15:34 +02:00
c8bca487e5 feat: Implement methods in AzureDevopsProvider for publishing code suggestions, labels, and removing comments 2024-01-08 08:59:12 +02:00
Tal
78fa61eac6 Merge branch 'main' into tr/user_description 2024-01-07 22:55:18 -08:00
Tal
582cbd623f Merge pull request #580 from Codium-ai/tr/add_logs
feat: Add debug logs to git_provider and pr_description modules
2024-01-07 09:58:20 -08:00
3ea08a6cf5 feat: Add debug logs to git_provider and pr_description modules 2024-01-07 19:57:49 +02:00
3154ebbf9f feat: Add debug logs to git_provider and pr_description modules 2024-01-07 19:56:05 +02:00
Tal
8fe608884d Merge pull request #578 from Codium-ai/ok/atlassian_version
feat: Update atlassian-python-api version in requirements.txt
2024-01-07 09:17:56 -08:00
60e79f0134 feat: Update atlassian-python-api version in requirements.txt 2024-01-07 18:08:11 +02:00
a6bbd04efb s 2024-01-07 17:02:18 +02:00
578d15c6fc usage guide 2024-01-07 16:38:08 +02:00
22d17985a1 Less noisy fallback for publish_code_suggestions in case of invalid comments
As a first option, `publish_code_suggestions` will try to post all review comments in a single GitHub review. This is preferred because it will group all comments together in the GitHub UI under the same review, and will trigger just one notification for any viewers of the PR.

If just one of the comments is malformed, the entire API request will fail and none of the comments will be posted to the PR. In the current implementation, the fallback mechanism is to just post each comment separately with `try/except` and skip the invalid comments. This works, but potentially creates a lot of noise in the PR as each comment is posted as in a separate review, creating multiple notifications.

This suggested fallback is based on a similar idea, but without creating multiple review notifications. The it works is by iterating over the potential comments, and starting a PENDING review for the current comment. The review is not submitted and does not trigger a notification, but it is verified against the GitHub API, and so we can verify if the comment is valid. After checking all comments we then submit a single review with all the verified comments which is guaranteed to succeed.

The end result is having the exact same comments posted to the PR as with the current fallback method, but the downside is having twice as many API calls (for each comment we have 1 extra API call to delete the pending review).
2024-01-07 16:00:44 +02:00
Tal
faba5a224a Merge pull request #575 from Codium-ai/tr/user_description
docs: Enhance 'improve' tool documentation and update 'DESCRIBE.md' w…
2024-01-07 01:30:44 -08:00
19f85f08b0 docs: Enhance 'improve' tool documentation and update 'DESCRIBE.md' with hyperlink 2024-01-07 11:29:07 +02:00
8512e9dffb docs: Enhance 'improve' tool documentation and update 'DESCRIBE.md' with hyperlink 2024-01-07 11:26:24 +02:00
7c0be8ca44 docs: Enhance 'improve' tool documentation and update 'DESCRIBE.md' with hyperlink 2024-01-07 11:22:53 +02:00
d9872d7031 docs: Enhance 'improve' tool documentation and update 'DESCRIBE.md' with hyperlink 2024-01-07 11:19:47 +02:00
Tal
be4b9c4991 Merge pull request #574 from Codium-ai/tr/user_description
Enhance and reorganize documentation for review and describe tools
2024-01-07 00:01:30 -08:00
94172104f0 docs: Reorganize and enhance documentation for review and describe tools 2024-01-07 09:56:09 +02:00
8ec790d4f7 Merge remote-tracking branch 'origin/main' into tr/user_description
# Conflicts:
#	docs/DESCRIBE.md
2024-01-07 09:49:43 +02:00
640972b00a docs: Reorganize and enhance documentation for review and describe tools 2024-01-07 09:48:17 +02:00
7810ba7d76 docs: Reorganize and enhance documentation for review and describe tools 2024-01-07 09:40:24 +02:00
d91f1fce09 docs: Reorganize and enhance documentation for review and describe tools 2024-01-07 09:36:03 +02:00
edcb666fbc docs: Reorganize and enhance documentation for review and describe tools 2024-01-07 09:28:06 +02:00
Tal
6d359fe1d8 Update DESCRIBE.md 2024-01-06 21:46:11 +02:00
Tal
19e38595f3 Merge pull request #573 from Codium-ai/tr/user_description
feat: Update adaptive collapsible file list logic in pr_description.py
2024-01-06 11:08:55 -08:00
355ef8c476 feat: Update adaptive collapsible file list logic in pr_description.py 2024-01-06 10:36:36 +02:00
Tal
f08d225360 Merge pull request #572 from Codium-ai/tr/user_description
describe docs
2024-01-06 00:15:36 -08:00
22b7dd9f2d s 2024-01-06 10:15:04 +02:00
a85a328791 s 2024-01-06 10:08:29 +02:00
82d10f24f0 s 2024-01-06 10:04:48 +02:00
Tal
c9717f1d73 Update README.md 2024-01-05 23:29:59 +02:00
Tal
82b58d5b09 Update README.md 2024-01-05 21:37:45 +02:00
Tal
b6d41d6a91 Merge pull request #571 from Codium-ai/tr/user_description
Enhance Documentation and Configuration of 'describe' Tool
2024-01-05 11:30:41 -08:00
ac74fa8431 docs 2024-01-05 21:28:54 +02:00
42704d5781 Merge remote-tracking branch 'origin/main' into tr/user_description 2024-01-05 17:03:30 +02:00
3628786a61 feat: Clarify PRType label member usage in pr_description_prompts.toml 2024-01-05 17:03:14 +02:00
Tal
c69ede0138 Update REVIEW.md 2024-01-05 11:10:55 +02:00
Tal
4349156e97 Merge pull request #570 from wesvetter/patch-1
📚 Minor update to REVIEW.md for inclusive language
2024-01-04 22:48:36 -08:00
9f88105f72 📚 Minor update to REVIEW.md for inclusive language
Replaces "his [judgement]" with "their".
2024-01-04 10:32:53 -08:00
Tal
fe6b2065fb Merge pull request #569 from zmeir/zmeir/enhance/auto_improve_extended_simple
Add toggle to automatically enable `/improve --extended`
2024-01-04 09:06:08 -08:00
c2b0891c0b Simpler auto-extended toggle and keep the default as false 2024-01-04 18:53:45 +02:00
Tal
782f1ca1bd Merge pull request #568 from Codium-ai/tr/user_description
Enhancement: Improved Extraction and Placement of User Descriptions in PRs
2024-01-04 08:35:34 -08:00
6d18a0c843 feat: Improve user description extraction in git_provider.py 2024-01-04 18:34:00 +02:00
Tal
e6093cd768 Merge pull request #567 from zmeir/zmeir/enhance/support_azure_in_langchain_ai_handler
Add support for Azure OpenAI in LangChainOpenAIHandler
2024-01-04 08:22:01 -08:00
Tal
aea0c4d45f Merge pull request #566 from zmeir/zmeir/fix/config_command_missing_arg
Fix failing `/config` command
2024-01-04 08:14:29 -08:00
1c2bb2ef3d feat: Update user description extraction and placement in PR description 2024-01-04 18:05:28 +02:00
7762bf59bf feat: Update user description extraction and placement in PR description 2024-01-04 18:01:55 +02:00
3e29848cd0 Merge remote-tracking branch 'origin/main' into tr/user_description
# Conflicts:
#	pr_agent/git_providers/git_provider.py
2024-01-04 17:49:10 +02:00
c3b5aaf8cc feat: Update user description extraction and placement in PR description 2024-01-04 17:46:24 +02:00
ba3f22d81e Move logging to a central location for all AI Handlers 2024-01-04 16:22:22 +02:00
ac7aaa0cd3 Add support for Azure OpenAI in LangChainOpenAIHandler 2024-01-04 16:22:22 +02:00
1ade09eaa3 Fix failing /config command
All commands need the `ai_handler` argument. The PRConfig class was missing it in the `__init__` method and so it failed with this error:

```
File "/home/vcap/app/pr_agent/agent/pr_agent.py", line 76, in handle_request
    await command2class[action](pr_url, ai_handler=self.ai_handler, args=args).run()
TypeError: PRConfig.__init__() got an unexpected keyword argument 'ai_handler'
```
2024-01-04 14:49:34 +02:00
Tal
b7af45166a Merge pull request #561 from zmeir/zmeir/fix/get_user_description
Fix `get_user_description`
2024-01-04 00:40:08 -08:00
Tal
92f89e6ca0 Merge pull request #565 from Codium-ai/tr/remove_old_walkthrough
Remove old 'enable_file_walkthrough' mode
2024-01-04 00:36:05 -08:00
ed78bfd946 use_collapsible_file_list 2024-01-04 10:27:07 +02:00
4204d78d7e feat: Remove file walkthrough feature from PR agent 2024-01-04 09:59:44 +02:00
3c2ed8bbf1 feat: Remove file walkthrough feature from PR agent 2024-01-04 09:42:15 +02:00
8d2da74380 Find user description in a case-insensitive way 2024-01-04 09:41:55 +02:00
39c1866121 Revert title() to capitalize() 2024-01-04 09:41:24 +02:00
Tal
1bba0162f8 Merge pull request #563 from zmeir/zmeir/enhance/refine_add_docs_prompt
Refine the prompt for `add_docs` command
2024-01-03 23:14:27 -08:00
Tal
c07ea5ea32 Merge pull request #560 from zmeir/zmeir/fix/drop_python3.9
Drop support for python 3.9
2024-01-03 23:09:54 -08:00
2f9fbbf0ac Prevent reducing the number of suggestions if already low enough 2024-01-03 16:43:39 +02:00
0189e12fb1 Automatically enable improve extended mode for large PRs 2024-01-03 16:43:38 +02:00
58f93e0615 Drop support for python 3.9
The `bitbucket_server_provider.py` uses structural pattern matching that was introduced in python 3.10, and so trying to run any command with python 3.9 will fail (because we import all the providers right at the top of `pr_agent.git_providers`)
2024-01-03 12:30:09 +02:00
967494ce17 Refine the prompt for add_docs command
I found that without it, python docstrings are sometimes suggested above the function signature, instead of below.
2024-01-03 12:27:23 +02:00
560d30dbb1 Fix get_user_description
The headers changed from "PR Type"/"PR Description"/etc to "Type"/"Description"/etc
2024-01-03 12:20:51 +02:00
Tal
c31ce3de35 Merge pull request #559 from pzarfos/fix/prompt-spelling
feat: Fix typo
2024-01-02 21:41:34 -08:00
0bd2f045a3 feat: Fix typo 2024-01-02 08:11:31 -05:00
Tal
0b4a98b3aa Merge pull request #558 from Codium-ai/tr/soc2_ticket
feat: Add SOC2 compliance review feature to PR agent 💎
2024-01-01 22:02:22 -08:00
7dfc306e7c feat: Add SOC2 compliance review feature to PR agent 2024-01-01 20:15:36 +02:00
Tal
be88624e2a Merge pull request #556 from Codium-ai/mrT23-patch-1
Update Usage.md
2023-12-31 07:35:40 -08:00
Tal
ac9a46d4c4 Update Usage.md 2023-12-31 17:34:13 +02:00
3e1349ed1f Merge pull request #554 from xens/patch-1
fix: missing flag in INSTALL.md
2023-12-28 10:29:14 -08:00
0d89e6e760 fix: missing flag in INSTALL.md
Fixing a missing flag on the Docker cli to declare a variable.
2023-12-28 17:24:08 +01:00
a9c8fb6a73 Merge pull request #552 from KennyDizi/main
Add `enable_help_text` setting and update PR review preparation method
2023-12-26 21:45:21 -08:00
fce52a66ff feat: Update enable_help_text flag explaination 2023-12-27 10:22:43 +07:00
dff31ff8f5 feat: Fix typo 2023-12-27 10:17:56 +07:00
37b040b50a Use 'and' in lieu of '&' operator 2023-12-27 10:17:08 +07:00
31168cd7de Get PR review enable help text from setting 2023-12-27 10:12:41 +07:00
db6ca434ac Update Usage.md 2023-12-26 17:15:48 +02:00
958bfe1000 Merge pull request #551 from Codium-ai/tr/global_configuration
global configuration
2023-12-26 07:08:52 -08:00
815862e428 markdown 2023-12-26 17:06:29 +02:00
b1ce29e27a global configuration 2023-12-26 16:45:39 +02:00
f7c2b3128f Merge pull request #550 from Codium-ai/tr/gfm_markdown
feat: Refactor help text addition in pr_reviewer.py and update tool n…
2023-12-25 23:30:36 -08:00
a6764c9058 feat: Update help text addition condition in pr_reviewer.py 2023-12-26 09:25:15 +02:00
a854e1a408 feat: Refactor help text addition in pr_reviewer.py and update tool names in README.md 2023-12-26 09:18:38 +02:00
ba3a8b24f0 Merge pull request #548 from PrashantDixit0/main
LanceDB Integration
2023-12-25 06:39:11 -08:00
26cb85c4f5 default pinecone 2023-12-25 14:50:15 +05:30
1d435ef3fa removed comments 2023-12-25 00:45:24 +05:30
1632696c2f Merge branch 'main' of github.com:PrashantDixit0/pr-agent 2023-12-25 00:41:28 +05:30
d8d954bb0f lancedb integration 2023-12-25 00:38:24 +05:30
08e9a91021 Merge pull request #547 from Codium-ai/tr/readme_update
Readme for PR-Agent-Pro
2023-12-24 06:30:04 -08:00
648c22ed1e tools update 2023-12-24 16:22:45 +02:00
49592ba2d7 docs: Refine markdown formatting in Analyze.md and README.md 2023-12-24 16:21:41 +02:00
0c4d451d9a readme 2023-12-24 16:18:20 +02:00
e698c7e2f3 Merge pull request #546 from Codium-ai/tr/backticks_review
Single-label for suggestions
2023-12-24 03:35:49 -08:00
663632e2d9 fixed bug 2023-12-24 10:27:48 +02:00
5fd3fdfae1 feat: Add loop to populate suggestion_list in pr_code_suggestions.py 2023-12-24 10:08:36 +02:00
47b267a73d prompt 2023-12-24 09:52:59 +02:00
5c49ff216a feat: Update inline comment creation in git providers and improve code suggestion handling
- Update `create_inline_comment` method in various git providers to include `absolute_position` parameter
- Remove `create_inline_comment` method from providers that do not support inline comments
- Enhance `find_line_number_of_relevant_line_in_file` function to handle absolute position
- Modify `pr_code_suggestions.py` to handle improved code inclusion in suggestions
- Add `include_improved_code` configuration option in `configuration.toml` and update documentation accordingly
2023-12-24 09:44:08 +02:00
5dc2595dcf feat: Refactor code suggestion handling and update YAML schema in pr_code_suggestions.py and pr_code_suggestions_prompts.toml
- Update key names in pr_code_suggestions.py to use snake_case for consistency
- Implement removal of invalid suggestions where existing code is equal to improved code
- Update YAML parsing in _prepare_pr_code_suggestions method to include keys_fix_yaml parameter
- Refactor push_inline_code_suggestions method to use updated key names
- Update _prepare_prediction_extended method to use new key names
- Refactor _prepare_markdown method to include suggestion label and use updated key names
- Update instructions and YAML schema in pr_code_suggestions_prompts.toml to reflect changes in pr_code_suggestions.py
- Remove redundant removal of invalid suggestions in rank_suggestions method
2023-12-24 08:30:35 +02:00
664b1c9d17 Merge pull request #545 from Codium-ai/tr/backticks_review
feat: Improve suggestion formatting in markdown text generation
2023-12-23 10:41:17 -08:00
ba7781ba00 feat: Update instruction formatting in pr_code_suggestions_prompts.toml and pr_reviewer_prompts.toml 2023-12-23 20:40:30 +02:00
42be96a99b feat: Improve suggestion formatting in markdown text generation 2023-12-23 20:32:03 +02:00
64a2c55d48 Merge pull request #542 from Codium-ai/tr/title_last
Enhancement: Update PR description prompts and reorder keys in PR description data
2023-12-21 03:55:17 -08:00
eca8078071 feat: Reorder keys in PR description data and update PRDescription model in toml file 2023-12-21 08:51:57 +02:00
9995ccd4c7 feat: Update PR description prompts in toml file to include semantic file types and custom labels options 2023-12-21 08:31:54 +02:00
851c001aa5 Merge pull request #541 from Codium-ai/tr/changes
feat: Enhance YAML parsing with additional fallbacks and key customiz…
2023-12-20 22:26:15 -08:00
2b23700aec feat: Enhance YAML parsing with additional fallbacks and key customization in load_yaml and try_fix_yaml functions 2023-12-21 08:24:07 +02:00
553dad0bee feat: Enhance YAML parsing with additional fallbacks and key customization in load_yaml and try_fix_yaml functions 2023-12-21 08:21:34 +02:00
37259e550f Merge pull request #540 from Codium-ai/tr/backticks
Enhancement of PR Description Formatting and Instructions Update
2023-12-20 22:16:53 -08:00
66cbd6ef8f Merge pull request #537 from koid/feature/ignore-header-description-in-ai-response
Enhancement of AI Response Parsing Mechanism
2023-12-20 22:15:43 -08:00
a9d789978b fix: remove last line 2023-12-21 11:11:46 +09:00
f99862088e re-implemented test case 2023-12-21 11:09:25 +09:00
e2797ad09a re-implemented YAML extraction as a fallback 2023-12-21 11:06:41 +09:00
ccb116922f Merge pull request #529 from lukefx/bitbucket_webhook_improvements
feat: Improved server, security and commands
2023-12-20 17:27:06 +02:00
c079deba21 feat: Enhance PR description formatting and update instructions in pr_description_prompts.toml 2023-12-20 16:45:21 +02:00
16b61eb4e8 ignore header description in ai response 2023-12-20 11:50:27 +09:00
68c26b362b Merge pull request #533 from Codium-ai/hl/fix_incomplete_yaml
bug fix
2023-12-18 10:07:07 -08:00
6e63cf4014 Add log 2023-12-18 17:35:04 +02:00
c59e9f77a6 fix 2023-12-18 17:06:02 +02:00
2a3779776a Merge pull request #532 from Codium-ai/hl/native_labels_readme
Hl/native labels readme
2023-12-18 16:12:38 +02:00
e25980f141 fix: using the same get_settings convention 2023-12-18 14:58:25 +01:00
6c80fde6df fix 2023-12-18 13:44:37 +02:00
75dcb035a7 Update 2023-12-18 13:41:50 +02:00
2ac86f429f Merge pull request #531 from Codium-ai/disable_github_action
Update pr-agent-review.yaml
2023-12-18 13:39:09 +02:00
9d2003d789 Update pr-agent-review.yaml 2023-12-18 13:38:06 +02:00
d2aef95847 Merge pull request #530 from Codium-ai/tr/labels
Enhancement: Implement label case conversion and update label descriptions in settings files
2023-12-18 03:21:48 -08:00
1c4e64333c feat: Implement label case conversion and update label descriptions in settings files 2023-12-18 12:29:06 +02:00
f121a420c9 Add to describe 2023-12-18 10:08:29 +02:00
3b13738943 Add docs to custom labels page 2023-12-18 10:04:05 +02:00
7a5acb29ac feat: Improved server, security and commands
Signed-off-by: Luca Simone <info@lucasimone.info>
2023-12-17 17:38:27 +01:00
ce35addcd3 Merge pull request #528 from Codium-ai/tr/lazy_init
Refactor AI handler instantiation to use lazy initialization in PR tools
2023-12-17 07:01:24 -08:00
5fb373b212 Refactor AI handler instantiation to use lazy initialization in PR tools 2023-12-17 16:52:03 +02:00
54891ad1d2 Merge pull request #514 from brianpham93/abstract-BaseAiHandler
Abstract AiHandler to BaseAiHandler
2023-12-14 07:54:13 -08:00
02871b1e3d Remove logging from pr_agent.py and add line breaks in cli.py and github_action_runner.py 2023-12-14 09:08:47 +02:00
38ea9143f3 Make LangChain dependency optional in pr-agent and update requirements.txt 2023-12-14 09:05:53 +02:00
246be6147f Set LiteLLMAIHandler as default AI handler in all PR tools and simplify AI handler injection in PRAgent 2023-12-14 09:00:14 +02:00
3531016a2c Refactor AI handler instantiation in PRAgent and related classes 2023-12-14 08:53:22 +02:00
e37598fdca Merge remote-tracking branch 'upstream/main' into abstract-BaseAiHandler 2023-12-14 07:45:43 +08:00
557b39ec87 Merge branch 'base-ai-handler' into abstract-BaseAiHandler 2023-12-14 07:44:13 +08:00
69a7c77a0d Refactor PRAgent class and has_ai_handler_param
method

This commit refactors the PRAgent class and the has_ai_handler_param
method. The has_ai_handler_param method is moved outside the class and
made a standalone function. This change improves code organization and
readability. The has_ai_handler_param function now takes a class object
as a parameter and checks if the class constructor has an "ai_handler"
parameter. This refactoring is done to streamline the code and improve
its maintainability.

No issue references.
2023-12-14 07:15:56 +08:00
2a8c2e488a Merge pull request #524 from Codium-ai/hl/native_labels_gitlab
feat: Add repository labels retrieval function in gitlab_provider.py
2023-12-13 17:36:18 +02:00
89c30ab5dc feat: Add repository labels retrieval function in gitlab_provider.py 2023-12-13 17:21:58 +02:00
ebb2ed891b Add logging to pr_agent.py 2023-12-13 08:16:45 +08:00
be8d6af87f Add code documentation generation for PR diffs 2023-12-13 08:16:31 +08:00
8fb4a42ef1 Update AI handler instantiation in server files 2023-12-13 08:16:02 +08:00
ca1ccd7b91 update base 2023-12-12 23:56:20 +08:00
b7225cc674 update langchain 2023-12-12 23:52:50 +08:00
a627dcd64f Update langchain 2023-12-12 23:28:58 +08:00
0c66554d50 langchain: move model and temperature to chat_completion 2023-12-12 23:07:46 +08:00
506eafc0c5 add langchain in requirement 2023-12-12 23:04:01 +08:00
6c7beccb4f add LangChain AI Handler 2023-12-12 23:03:49 +08:00
7eb2e769cf Move ai handlers to specific folder 2023-12-12 23:03:38 +08:00
5239e1c3e9 Load default AI Handler from util function 2023-12-12 21:51:05 +08:00
648dd3299f Merge pull request #521 from Codium-ai/tr/bitbucket_app
feat: Enable PR description publishing as comment in bitbucket_app.py
2023-12-12 00:27:48 -08:00
77a6fafdfc feat: Update Usage.md with limitations of bitbucket platform in auto_describe tool usage 2023-12-12 10:26:09 +02:00
ea7511e3c8 feat: Update Usage.md with limitations of bitbucket platform in auto_describe tool usage 2023-12-12 10:23:37 +02:00
512c92fe51 feat: Enable PR description publishing as comment in bitbucket_app.py 2023-12-12 10:19:17 +02:00
1853b4ef47 Merge pull request #520 from Codium-ai/tr/bitbucket_app
Refactor auto tool execution order and enhance logging and documentation
2023-12-12 00:02:02 -08:00
2f10b4f3c5 feat: Refactor auto tool execution order in bitbucket_app.py, add logging in pr_description.py, and update tool configuration instructions in Usage.md 2023-12-12 09:59:26 +02:00
73a20076eb Merge pull request #519 from Codium-ai/tr/bitbucket_app
Enhancement: Automatic Tool Configuration for Bitbucket App
2023-12-11 23:26:30 -08:00
afb633811f remove bad default 2023-12-12 09:18:51 +02:00
81da328ae3 feat: Add automatic tool configuration for Bitbucket app in bitbucket_app.py and configuration.toml, update Usage.md 2023-12-12 08:06:20 +02:00
729f5e9c8e Merge pull request #518 from Codium-ai/hl/github_native_labels
Refactoring Label Handling Across Git Providers
2023-12-11 16:50:19 +02:00
fdc776887d Refactor labels 2023-12-11 16:47:38 +02:00
cb64f92cce Merge pull request #511 from Codium-ai/tr/local_settings_on_push
Enhancement: Apply Repository Settings on Every 'Synchronize' Event
2023-12-11 06:27:29 -08:00
f3ad0e1d2a Merge pull request #517 from Codium-ai/tr/main_tmp
Improve PR description formatting and handling in pr_description.py
2023-12-11 06:15:46 -08:00
480e2ee678 feat: Improve PR description formatting in pr_description.py 2023-12-11 15:55:04 +02:00
9b97073174 s 2023-12-11 12:00:44 +02:00
4271bb7e52 Merge pull request #516 from Codium-ai/coditamar-readme-clarifications
Refine README.md for clarity and precision
2023-12-11 01:53:46 -08:00
e9bf8574a8 Update README.md 2023-12-11 11:52:36 +02:00
ebf7027aab add openai handler 2023-12-11 17:49:20 +08:00
a1cbd80b2a update base ai handler 2023-12-11 17:49:09 +08:00
2ce4af16cb Update README.md
fix grammar according to PR-Agent suggestions
2023-12-11 11:10:27 +02:00
2c1dfe7f3f Update README.md 2023-12-11 10:58:30 +02:00
b8021d7ca3 rename file 2023-12-11 16:57:23 +08:00
523a896465 Rename AiHandler to LiteLLMAiHandler 2023-12-11 16:56:49 +08:00
f7a6348401 Merge pull request #515 from Codium-ai/tr/review_graphics
Enhancement of Code Feedback Formatting in utils.py
2023-12-10 22:37:18 -08:00
02c0c89b13 feat: Add exception handling for discussion creation in gitlab_provider.py 2023-12-11 08:29:09 +02:00
b8cc110cbe s 2023-12-10 19:51:08 +02:00
2b1e841ef1 s 2023-12-10 19:45:54 +02:00
a247fc3263 s 2023-12-10 17:46:49 +02:00
654938f27c feat: Enhance code feedback formatting in utils.py 2023-12-10 17:30:27 +02:00
b6409929d2 Remove extra code 2023-12-09 16:51:50 +00:00
c0303ff9ec Merge remote-tracking branch 'upstream/main' into abstract-BaseAiHandler 2023-12-09 16:47:13 +00:00
f2abe5c73e Abstract AiHandler to BaseAiHandler 2023-12-09 16:39:25 +00:00
7e47baa9db Refactor AI handler classes 2023-12-10 00:25:25 +08:00
a7a0de764c Merge pull request #512 from Codium-ai/trlabeling_files_extended
Refactoring and Enhancement of PR Description Formatting in 'pr_description.py'
2023-12-07 05:38:37 -08:00
1b22e59b4b feat: Update RELEASE_NOTES.md with version 0.11 details 2023-12-07 15:35:48 +02:00
f908d02ab4 readme 2023-12-07 15:26:36 +02:00
7d2a35e32c final commit 2023-12-07 10:27:19 +02:00
e351428848 s 2023-12-07 10:24:36 +02:00
4cd6649a44 feat: Enhance PR description formatting in pr_description.py
Improve the table structure for relevant files in PR description by adjusting the header and filename display. Add padding for filename and change summary, and move diff_plus_minus to a separate column. Refactor _insert_br_after_x_chars function to accept a variable length parameter.
2023-12-07 10:14:18 +02:00
e62acef6d2 s1 2023-12-07 09:50:36 +02:00
a043eb939b feat: Apply repo settings on push trigger in github_app.py 2023-12-07 08:42:18 +02:00
73eafa2c3d Merge pull request #509 from Codium-ai/trlabeling_files_extended
Enhancement of PR Description and Labeling Features
2023-12-06 07:05:22 -08:00
a61e492fe1 feat: Refactor PR files processing into separate function in pr_description.py 2023-12-06 17:01:21 +02:00
243f0f2b21 Update INSTALL.md 2023-12-06 16:52:47 +02:00
93b6d31505 s 2023-12-06 16:36:27 +02:00
429aed04b1 s 2023-12-06 16:32:53 +02:00
eeb20b055a feat: Add line count to file patch info and enhance PR description formatting 2023-12-06 15:29:45 +02:00
4b073b32a5 feat: Enhance PR description with file label dictionary and update prompts in pr_description_prompts.toml 2023-12-06 12:30:51 +02:00
f629755a9a feat: Refine field descriptions in pr_description_prompts.toml for semantic file labels 2023-12-06 10:59:44 +02:00
c1ed3ee511 feat: Refine field descriptions in pr_description_prompts.toml for semantic file labels 2023-12-06 08:08:01 +02:00
a4e6c99c82 Merge branch 'main' into hl/labeling_files 2023-12-05 08:28:59 -08:00
0b70e07b8c feat: Improve formatting in help.py command descriptions 2023-12-05 18:26:35 +02:00
862c236076 s 2023-12-05 18:10:13 +02:00
5c2f81a928 Merge pull request #508 from Codium-ai/trt/fix_describe
docs: Update issue link in INSTALL.md and enhance key formatting in p…
2023-12-05 07:01:07 -08:00
b76bc390f1 Merge pull request #503 from pzarfos/python312
Update dependencies in requirements.txt for Python 3.12
2023-12-05 06:55:19 -08:00
25d1e84b7f docs: Update issue link in INSTALL.md and enhance key formatting in pr_description.py 2023-12-05 16:54:18 +02:00
70a409abf1 Merge pull request #450 from koid/fix/specify-cache-directory-in-azure-devops
Specify Cache Directory in Azure DevOps in Lambda Function Setup
2023-12-05 06:48:44 -08:00
cf3401536a feat: Remove 'Refactoring' label from custom labels and update related descriptions 2023-12-05 07:48:21 +02:00
2feaee4306 feat: Update field descriptions in pr_description_prompts.toml for clarity 2023-12-04 21:45:22 +02:00
863eb0105d feat: Refactor semantic labels in PR description and improve clarity in pr_description.py and pr_description_prompts.toml 2023-12-04 21:23:59 +02:00
21a7a0f136 feat: Enhance link generation for relevant lines and refactor code in git providers and PR description tools 2023-12-04 21:06:56 +02:00
d2a129fe30 Add labeling files 2023-12-04 18:22:35 +02:00
fe796245a3 Merge pull request #501 from Codium-ai/tr/prompt_tuning
Refactoring and Enhancement of PR Agent Prompts
2023-12-04 03:18:12 -08:00
10bc84eb5b Merge pull request #505 from Codium-ai/mrT23-patch-1
Update Usage.md
2023-12-04 01:15:06 -08:00
06c0a35a65 Update Usage.md 2023-12-04 11:11:02 +02:00
082bcd00a1 Merge pull request #502 from pzarfos/local_models
Add descriptive error message for missing model in MAX_TOKENS array
2023-12-03 21:26:21 -08:00
71b421efa3 Updated requirements.txt for Python 3.12 2023-12-03 21:54:19 -05:00
317fec0536 Throw descriptive error message if model is not in MAX_TOKENS array 2023-12-03 21:06:55 -05:00
4dcbce41c8 feat: Refine prompts and improve formatting in pr_sort_code_suggestions_prompts.toml and pr_update_changelog_prompts.toml 2023-12-03 17:27:22 +02:00
b3fa654446 feat: Refactor prompts and improve formatting in pr_questions_prompts.toml 2023-12-03 17:23:52 +02:00
e09439fc1b feat: Enhance formatting and clarity in pr_information_from_user_prompts.toml 2023-12-03 17:17:24 +02:00
324e481ce7 feat: Improve formatting and clarity in pr_custom_labels.toml 2023-12-03 17:15:29 +02:00
abfad088e3 feat: Refine prompts and instructions in pr_add_docs.toml 2023-12-03 17:10:33 +02:00
f30789e6c8 feat: Refactor and enhance prompts in pr_code_suggestions_prompts.toml 2023-12-03 16:59:47 +02:00
5c01f97f54 feat: Enhance PR description prompts in pr_description_prompts.toml 2023-12-03 16:48:26 +02:00
2d726edbe4 feat: Improve formatting and clarity in pr_reviewer_prompts.toml 2023-12-03 16:40:06 +02:00
526ad00812 Merge pull request #500 from Codium-ai/tr/describe_message
Enhance pr_url assignment in github_provider.py for GitHub Actions compatibility
2023-12-03 02:05:38 -08:00
37812dfede feat: Update pr_url assignment in github_provider.py for GitHub Actions compatibility 2023-12-03 11:34:17 +02:00
c9debc38f2 Merge pull request #499 from Codium-ai/tr/describe_message
final update message in PR description
2023-12-03 01:02:21 -08:00
fe7d2bb924 update docs 2023-12-03 10:52:00 +02:00
586785ffde feat: Add pr_url attribute to git providers and final update message in PR description 2023-12-03 10:46:02 +02:00
c21e606eee Merge pull request #498 from Codium-ai/tr/update_pics
Update Image URLs in Markdown Documentation
2023-12-03 00:11:13 -08:00
a658766046 Merge pull request #495 from Codium-ai/tr/review_protection
Enhanced Exception Handling and Code Suggestion Formatting
2023-12-03 00:11:02 -08:00
3f2175e548 diagram 2023-12-03 09:12:10 +02:00
3ff30fcc92 fix logo for non-github image 2023-12-03 08:58:28 +02:00
07abf4788c docs: Update image URLs in markdown files 2023-12-03 08:41:09 +02:00
492dd3c281 docs: Update Azure setup instructions in Usage.md 2023-12-03 08:25:12 +02:00
d2fb1cfce5 Merge pull request #497 from network-charles/network-charles-patch-1
Update pr-agent/docs/Review.md Image
2023-12-02 22:18:56 -08:00
24fe5a572a Update REVIEW.md 2023-12-02 10:36:37 +01:00
3af9c3bfb9 feat: Enhance code suggestion publishing with language-specific formatting in pr_code_suggestions.py 2023-12-01 12:12:49 +02:00
c22084c7ac feat: Add exception handling for missing previous review in github_provider.py 2023-12-01 11:56:03 +02:00
96b91c9daa feat: Add dynaconf to requirements.txt 2023-11-30 18:29:27 +02:00
55464d5c5b Merge pull request #492 from Codium-ai/mrT23-patch-1
Update IMPROVE.md
2023-11-30 18:19:36 +02:00
f6048e8157 Merge pull request #490 from network-charles/network-charles-patch-1
Alphabetical Reordering of requirements.txt
2023-11-30 08:16:01 -08:00
e474982485 Merge pull request #491 from network-charles/network-charles-patch-2
Update INSTALL.md
2023-11-30 07:18:48 -08:00
18f06cc670 Update IMPROVE.md 2023-11-30 17:13:47 +02:00
574e3b9d32 Update IMPROVE.md 2023-11-30 16:35:44 +02:00
f7410da330 Update INSTALL.md 2023-11-30 14:13:20 +01:00
76f3d54519 Update INSTALL.md 2023-11-30 13:56:22 +01:00
59f117d916 Update INSTALL.md 2023-11-29 16:07:57 +01:00
4a71259be7 Update requirements.txt 2023-11-29 15:47:53 +01:00
b90dde48c0 Merge pull request #483 from tmokmss/add-bedrock-support
Add Amazon Bedrock support
2023-11-29 03:08:01 -08:00
c57807e53a Merge pull request #489 from Codium-ai/ok/gh_actions_fix
Improve error handling in settings retrieval
2023-11-29 11:55:22 +02:00
0e54a13272 Improve error handling in settings retrieval
Fix bug where default values were not being used in GitHub Action runners when environmental variables are not set. Now, if an environmental variable cannot be found and no default is given, the default value will be used if one exists. This will prevent errors during setup on different environments and ensure system stability.
2023-11-29 11:52:02 +02:00
ddc6c02018 Merge pull request #488 from Codium-ai/ok/gh_actions_fix
"Add fallback to environment variables in GitHub Action Runner"
2023-11-29 11:49:40 +02:00
b67d06ae59 "Add fallback to environment variables in GitHub Action Runner"
A new function `get_setting_or_env` was implemented to facilitate fetching of either settings or environmental variables in the GitHub Action Runner. This was necessary to resolve an issue where a certain undefined behaviour occurs in GitHub Actions, leading to an attribute error. The new function also provides a default value parameter to ensure the return of a value even in failed attempts to fetch from either settings or environment variables.
2023-11-29 11:47:52 +02:00
ca1289af03 Update pr-agent-review.yaml 2023-11-29 11:03:24 +02:00
5e642c10fa fallback to try_fix_yaml 2023-11-29 17:57:54 +09:00
b5a643d67a Merge pull request #487 from Codium-ai/ok/gh_actions_fix
Add utility function to handle boolean type conversion
2023-11-29 10:38:39 +02:00
580eede021 Add utility function to handle boolean type conversion
A utility function (`is_true`) was added to take care of validating and converting boolean values from string or boolean types. This function is used in three parts of the `run_action` method where automatic PR review, description, and improvement actions are triggered based on environment settings. This change makes the condition checks cleaner and prevents code duplication.
2023-11-29 10:33:12 +02:00
ea56910a2f Merge pull request #485 from Codium-ai/tr/bitbucket
Enhancement of Inline Comment Publishing in Bitbucket Provider and Logging Addition
2023-11-28 08:35:40 -08:00
51e1278cd7 feat: Enhance inline comment publishing in Bitbucket provider and add logging for no suggestions in pr_code_suggestions.py 2023-11-28 18:29:35 +02:00
ddeb4b598d Merge pull request #484 from Codium-ai/tr/review_fix_tags
Refactor: Improved label handling in pr_reviewer.py
2023-11-28 07:14:29 -08:00
7e029ead45 refactor: Improve label handling in pr_reviewer.py 2023-11-28 17:03:55 +02:00
f8f57419c4 Update ai_handler.py 2023-11-28 23:08:17 +09:00
917f4b6a01 hard code value 2023-11-28 20:59:21 +09:00
97d6fb999a set max_tokens_to_sample 2023-11-28 20:58:57 +09:00
1373ca23fc support Amazon Bedrock 2023-11-28 20:58:42 +09:00
4521077433 Merge pull request #481 from Codium-ai/ok/fix_improve
Refactoring Environment Variables Access
2023-11-27 23:31:47 -08:00
6264624c05 Merge branch 'main' into ok/fix_improve 2023-11-27 07:27:25 -08:00
5f6fa5a082 Update Usage.md 2023-11-27 16:18:02 +02:00
2dcee63df5 fix improve 2023-11-27 12:32:53 +02:00
c6cc676275 Update pr-agent-review.yaml 2023-11-27 08:22:58 +02:00
b8e4d10b9d summarize=true 2023-11-27 07:36:02 +02:00
70a957caf0 AUTO_IMPROVE 2023-11-27 07:35:17 +02:00
5ff9aaedfd Update IMPROVE.md 2023-11-26 17:41:20 +02:00
fc8865f8dc Merge pull request #476 from Codium-ai/tr/improve_inplace
Enhancements and Bug Fixes in Code Suggestions and Line Link Generation
2023-11-26 07:32:37 -08:00
466af37675 s 2023-11-26 17:21:02 +02:00
2202ff1cdf s 2023-11-26 17:17:36 +02:00
2022018d4c update tests 2023-11-26 17:16:04 +02:00
b1c374808d feat: Add line link generation in Bitbucket provider and improve markdown formatting in pr_code_suggestions.py and IMRPOVE.md 2023-11-26 17:12:02 +02:00
20978402ea s 2023-11-26 16:56:06 +02:00
8f615e17a3 s 2023-11-26 16:42:41 +02:00
5cbbaf44c9 feat: Add line link generation for GitLab and improve markdown formatting in pr_code_suggestions.py 2023-11-26 13:42:57 +02:00
f96d4924e7 feat: Add line link generation in git providers and refactor code suggestions generation 2023-11-26 11:57:45 +02:00
f36b672eaa feat: Add option to summarize code suggestions in pr_code_suggestions.py 2023-11-26 11:22:14 +02:00
f104b70703 Update INSTALL.md 2023-11-26 10:38:49 +02:00
d4e979cb02 Merge pull request #447 from Codium-ai/tr/pydantic
Refactor PR label handling and update CLI commands
2023-11-25 23:37:02 -08:00
668041c09f Code suggestions guidelines: 2023-11-26 09:32:02 +02:00
aa73eb2841 PR 2023-11-26 09:24:33 +02:00
14d4ca8c74 PR 2023-11-26 09:22:19 +02:00
690c113603 refactor: Improve clarity and consistency in pr_code_suggestions_prompts.toml and pr_reviewer_prompts.toml files 2023-11-26 09:17:42 +02:00
1a28c77783 Previous description 2023-11-26 09:08:33 +02:00
0326b7e4ac refactor: Update PR prompts in toml files for clarity and consistency 2023-11-26 09:05:45 +02:00
d8ae32fc55 language_extension_map 2023-11-26 08:52:55 +02:00
8db2e3b2a0 feat: Enhance readability in toml files and add verbosity level logging in pr_generate_labels.py 2023-11-26 08:42:04 +02:00
9465b7b577 refactor: Move clip_tokens function from pr_processing to utils module, and add tests 2023-11-26 08:29:47 +02:00
d7df4287f8 feat: Update PR prompts in toml files to enhance readability and consistency 2023-11-26 08:17:16 +02:00
b3238e90f2 s 2023-11-26 08:10:01 +02:00
fdfd6247fb Merge branch 'main' into tr/pydantic 2023-11-25 21:36:16 -08:00
46d4d04e94 Merge pull request #455 from lukefx/bitbucket-server
Added BitBucket Server and Data Center support
2023-11-25 21:33:26 -08:00
0f6564f42d feat: Added server and documentation 2023-11-25 17:37:44 +01:00
cddf183e03 Merge pull request #470 from Codium-ai/tr/glob
Enhance glob pattern handling and logging in file filtering
2023-11-22 23:19:09 -08:00
e80a0ed9c8 glob 2023-11-23 09:16:50 +02:00
d6d362b51e Merge pull request #469 from Codium-ai/mrT23-patch-1
Improve Documentation in Usage.md
2023-11-22 22:14:22 -08:00
4eff0282a1 Update Usage.md 2023-11-23 08:06:07 +02:00
8fc07df6ef Update INSTALL.md 2023-11-21 18:39:36 +02:00
84e4b607cc Merge pull request #467 from Codium-ai/ok/base_url
Add support for base_url in GitHub SDK
2023-11-21 16:51:16 +02:00
613ccb4c34 Add support for base_url in GitHub SDK 2023-11-21 16:48:36 +02:00
e95a6a8b07 Merge pull request #466 from Codium-ai/ok/gitlab_fix
Fix a bug in GitLab webhook
2023-11-21 16:36:40 +02:00
2add584fbc Fix a bug in GitLab webhook 2023-11-21 16:28:01 +02:00
54d7d59177 Update Usage.md 2023-11-20 20:06:07 +02:00
b3129c7dd9 Merge pull request #464 from Codium-ai/tr/more_protections
Refactor YAML parsing for improved error handling
2023-11-20 02:28:57 -08:00
3f76d95495 ScannerError 2023-11-20 10:35:35 +02:00
1b600cd85f Refactor YAML parsing for improved error handling 2023-11-20 10:30:59 +02:00
26cc26129c Merge pull request #463 from Codium-ai/tr/more_protections
minor fix
2023-11-19 07:45:39 -08:00
d1d7903e39 minor fix 2023-11-19 17:44:11 +02:00
dff4d1befc Merge pull request #462 from Codium-ai/tr/more_protections
Enhancements in YAML Parsing and Error Handling
2023-11-19 07:40:06 -08:00
3504a64269 protections 2023-11-19 17:35:40 +02:00
83247cadec protections 2023-11-19 17:30:57 +02:00
5ca1748b93 Merge pull request #460 from Codium-ai/tr/update_instructions
GFM mode for 'review' instructions
2023-11-19 01:20:36 -08:00
c7a681038d gfm instructions 2023-11-19 11:11:11 +02:00
eb977b4c24 gfm instructions 2023-11-19 11:02:11 +02:00
b62e0967d5 fix: Revert back to exception since context.get will not throw KeyError 2023-11-17 10:08:40 +01:00
14a934b146 Update Usage.md 2023-11-17 10:41:52 +02:00
26dc2e9d21 fix: raising exception instead of empty string
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-16 11:19:46 +01:00
d78a71184d fix: Use checked exception KeyError for missing key
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-16 10:59:01 +01:00
eae30c32a2 fix: Use checked exception for ValueError
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-16 10:58:35 +01:00
bc28d657b2 Merge pull request #438 from koid/fix/remove-unnecessary-setup
Removal of Redundant Logger Setup
2023-11-15 10:35:13 -08:00
416a5495da Merge pull request #453 from Codium-ai/tr/v_010
Version 0.10 Release and Workflow Update
2023-11-15 09:46:31 -08:00
a2b27dcac8 v10 2023-11-15 19:45:51 +02:00
d8e4e2e8fd Merge pull request #454 from Codium-ai/coditamar-bitbucket-doc-type
Update INSTALL.md
2023-11-15 09:44:03 -08:00
3fae5cbd8d feat: Added BitBucket Server
Signed-off-by: Luca Simone <info@lucasimone.info>
2023-11-15 15:47:44 +01:00
896a81d173 Update INSTALL.md 2023-11-15 15:20:50 +02:00
b216af8f04 v10 2023-11-15 14:49:18 +02:00
388cc740b6 Merge pull request #436 from rhyst/support-vertex-ai
Support Google's Vertex AI
2023-11-15 04:26:08 -08:00
6214494c84 Merge pull request #452 from Codium-ai/tr/review_extra_labels
Add Review Labels for Security and Effort Estimation
2023-11-15 04:25:03 -08:00
762a6981e1 extra_labels 2023-11-15 14:12:59 +02:00
b362c406bc Merge remote-tracking branch 'origin/main' into tr/review_extra_labels 2023-11-15 14:07:44 +02:00
7a342d3312 extra_labels 2023-11-15 14:07:32 +02:00
2e95988741 extra_labels 2023-11-15 14:04:17 +02:00
9478447141 extra_labels 2023-11-15 14:02:13 +02:00
082293b48c Merge pull request #451 from Codium-ai/tr/persistent_enhacments
Enhancement of Persistent Comments in PR Review
2023-11-15 03:55:15 -08:00
e1d92206f3 docs 2023-11-15 13:32:32 +02:00
557ec72bfe Update documentation for Vertex AI 2023-11-15 10:27:48 +00:00
6b4b16dcf9 Support Google's Vertex AI 2023-11-15 10:26:58 +00:00
c4899a6c54 bitbucket 2023-11-15 12:11:02 +02:00
24d82e65cb gitlab 2023-11-15 09:45:10 +02:00
2567a6cf27 gitlab 2023-11-15 09:40:45 +02:00
94cb6b9795 more feedback 2023-11-15 09:06:26 +02:00
e878bbbe36 Merge pull request #449 from zmeir/patch-1
Fix `get_user_description` in case `pr_description.enable_pr_type=false`
2023-11-14 22:09:59 -08:00
172b5f0787 add instructions to lambda function section 2023-11-15 13:22:07 +09:00
0df0542958 prompt 2023-11-13 15:55:35 +02:00
7d89b82967 Fix get_user_description in case pr_description.enable_pr_type=false
Fixes an issue when getting the user description after a PR-Agent description was already generated, in case the configuration setting `pr_description.enable_pr_type` was `false`.
2023-11-13 14:41:14 +02:00
c5f9bbbf92 Merge pull request #448 from Codium-ai/hl/optional_custom_labels
remove the "one or more" for custom labels
2023-11-13 13:51:18 +02:00
a5e5a82952 s 2023-11-13 13:49:16 +02:00
ccbb62b50a remove the "one or more" for custom labels 2023-11-13 13:47:06 +02:00
a8dddd1999 prompt 2023-11-13 12:14:18 +02:00
f5c6dd55b8 triple quote 2023-11-13 12:04:58 +02:00
0e932af2e3 multi line 2023-11-13 12:01:08 +02:00
1df36c6a44 Merge pull request #446 from Codium-ai/tr/fix_cli_args
Handling CLI Arguments with Quotes in pr_agent
2023-11-12 17:29:38 +02:00
e9891fc530 s1 2023-11-12 16:37:53 +02:00
9e5e9afe92 Refactor CLI argument handling and request processing 2023-11-12 16:11:34 +02:00
5e43c202dd s1 2023-11-12 15:45:22 +02:00
727eea2b62 s1 2023-11-12 15:00:06 +02:00
37e6608e68 Merge pull request #444 from Codium-ai/tr/fallback_yaml
Implementing Fallback Mechanisms for YAML Parsing
2023-11-12 00:43:15 -08:00
f64d5f1e2a tests 2023-11-12 08:36:57 +02:00
8fdf174dec fallback 2023-11-10 18:44:19 +02:00
29d4f98b19 Merge pull request #441 from Codium-ai/tr/presistent_review
Add Persistent Review Feature to PR Agent
2023-11-09 05:26:51 -08:00
737792d83c publish_persistent_comment 2023-11-09 15:24:55 +02:00
7e5889061c publish_persistent_comment 2023-11-09 15:20:31 +02:00
755e04cf65 bitbucket finally works 2023-11-08 20:41:55 +02:00
44d6c95714 response 2023-11-08 20:38:18 +02:00
14610d5375 persistent
s
2023-11-08 20:16:08 +02:00
f9c832d6cb Merge pull request #439 from Codium-ai/tr/fixes_added_files
Enhancement of Patch Handling and PR Processing
2023-11-08 04:48:07 -08:00
c2bec614e5 s 2023-11-08 14:46:11 +02:00
49725e92f2 s 2023-11-08 14:41:15 +02:00
a1e32d8331 s 2023-11-08 14:36:59 +02:00
0293412a42 s 2023-11-08 14:31:08 +02:00
10ec0a1812 s 2023-11-08 14:21:03 +02:00
69b68b78f5 s 2023-11-08 14:17:59 +02:00
c5bc4b44ff fix added files 2023-11-08 12:51:30 +02:00
39e5102a2e fix added files 2023-11-08 12:47:18 +02:00
f0991526b5 remove unnecessary setup_logger 2023-11-08 16:56:44 +09:00
6c82bc9a3e Merge pull request #437 from Codium-ai/tr/new_gpt4
Introduce support for 'gpt-4-1106-preview' model and dynamic token limit calculation
2023-11-07 04:49:50 -08:00
54f41dd603 code 2023-11-07 14:41:15 +02:00
094f641fb5 code 2023-11-07 14:38:37 +02:00
a35a75eb34 get_max_tokens + added 'gpt-4-1106-preview' 2023-11-07 14:28:41 +02:00
5a7c118b56 Merge pull request #434 from Codium-ai/document_describe
Update DESCRIBE.md
2023-11-06 11:00:06 -08:00
cf9e0fbbc5 Update DESCRIBE.md 2023-11-06 17:55:58 +02:00
ef9af261ed Merge pull request #433 from Codium-ai/hl/user_labels
Keep user labels
2023-11-06 15:17:19 +02:00
ff79776410 Keep user labels 2023-11-06 15:14:08 +02:00
ec3f2fb485 Revert "generate labels keep user labels only"
This reverts commit 94a2a5e527.
2023-11-06 15:08:29 +02:00
94a2a5e527 generate labels keep user labels only 2023-11-06 15:07:06 +02:00
ea4bc548fc Merge pull request #432 from Codium-ai/hl/type_vs_labels
Support git providers with no label support
2023-11-06 14:38:29 +02:00
1eefd3365b Merge commit 'e352c98ce83bfbd99078f62d8705eb938a6ba5b5' into hl/type_vs_labels 2023-11-06 14:24:33 +02:00
db37ee819a support git providers with no label support 2023-11-06 14:11:49 +02:00
e352c98ce8 Merge pull request #431 from Codium-ai/hl/type_vs_labels
Refactoring PR Labels Handling and Display
2023-11-06 02:10:38 -08:00
e96b03da57 add configuration enable_pr_type 2023-11-06 11:58:26 +02:00
1d2aedf169 Don't Display pr labels in the text 2023-11-06 11:35:22 +02:00
4c484f8e86 Merge pull request #423 from zmeir/zmeir-external-incremental_review_thresholds
Implementing Thresholds for Incremental PR Reviews
2023-11-06 01:07:01 -08:00
8a79114ed9 Merge pull request #430 from Codium-ai/tr/fix_prompt
Fix PR Description Prompt and Data Preparation
2023-11-06 01:06:26 -08:00
cd69f43c77 Merge pull request #428 from Codium-ai/tr/fixes
Enhancements and Fixes in Bitbucket Provider
2023-11-06 01:06:15 -08:00
6d6d864417 fix prompt 2023-11-06 09:44:59 +02:00
b286c8ed20 Added documentation for the new configurations 2023-11-06 09:44:04 +02:00
7238c81f0c fix prompt 2023-11-06 09:41:26 +02:00
62412f8cd4 fix prompt 2023-11-06 09:38:39 +02:00
5d2bdadb45 fix prompt 2023-11-06 09:33:10 +02:00
06d030637c fix prompt 2023-11-06 09:32:46 +02:00
8e3fa3926a Extract incremental review checks to separate method 2023-11-06 09:21:22 +02:00
92071fcf1c Stack all incremental parameters 2023-11-06 09:13:04 +02:00
fed1c160eb files walkthrough bullets 2023-11-06 08:43:15 +02:00
e37daf6987 link to change 2023-11-06 08:27:34 +02:00
8fc663911f fixe bitbucket get_repo_settings bug 2023-11-06 08:15:43 +02:00
bb2760ae41 tools 2023-11-06 08:10:04 +02:00
3548b88463 type and labels 2023-11-05 15:48:39 +02:00
c917e48098 Merge pull request #427 from koid/fix/add-middleware
Adding Middleware to FastAPI Initialization
2023-11-05 01:40:37 -07:00
e6ef123ce5 add middleware when initializing fastapi 2023-11-05 15:38:19 +09:00
194bfe1193 Update INSTALL.md 2023-11-05 07:59:59 +02:00
e456cf36aa Merge pull request #425 from Codium-ai/ok/protect_apply_settings
Add exception handling for applying repo settings failure
2023-11-03 11:07:49 -07:00
fe3527de3c Add exception handling for applying repo settings failure 2023-11-03 12:23:49 +02:00
b99c769b53 Merge pull request #415 from zmeir/zmeir-patch-2
Refactor Command Handling for Different Triggers
2023-11-02 18:32:42 +02:00
60bdfb78df Merge pull request #424 from Codium-ai/ok/bitbucket_fix
Update Bitbucket Provider to Use 'position' Instead of 'start_line' for Inline Comments
2023-11-02 18:31:18 +02:00
c0b3c76884 Merge remote-tracking branch 'origin/main' into ok/bitbucket_fix 2023-11-02 15:27:11 +02:00
e1370a8385 Update publish_inline_comments in bitbucket_provider.py to use 'position' instead of 'start_line' 2023-11-02 15:24:47 +02:00
c623c3baf4 Added new configurations to prevent too frequent incremental commits on push trigger 2023-11-02 12:24:54 +02:00
d0f3a4139d Merge pull request #422 from Codium-ai/pr_review_fix_type_example
small fix to pr type example yaml
2023-11-02 11:48:49 +02:00
3ddc7e79d1 Update pr_reviewer_prompts.toml 2023-11-02 11:45:34 +02:00
3e14edfd4e Merge pull request #421 from zmeir/patch-1
Fix error in `get_main_pr_languages` when the diff is empty
2023-11-02 01:20:36 -07:00
15573e2286 Fix error in get_main_pr_languages when the diff is empty
This can happen for example when you have one commit add a line to a file and the next commit deletes that line. Then if those are the only 2 commits in the PR the diff will be empty.
2023-11-02 10:10:54 +02:00
ce64877063 Merge pull request #419 from KennyDizi/fix_synstax_error
Fix Syntax Error in f-string Expression
2023-11-02 08:23:17 +02:00
6666a128ee Update Usage.md 2023-11-01 18:36:12 +02:00
9fbf89670d Improve expression portion of f-strings 2023-11-01 19:11:52 +07:00
ad1c51c536 Fix SyntaxError: f-string expression part cannot include a backslash 2023-11-01 19:06:29 +07:00
9ab7ccd20d Merge pull request #416 from zmeir/patch-1
Fix formatting when last commit message contains _
2023-11-01 13:13:31 +02:00
c907f93ab8 Merge pull request #418 from KennyDizi/fix/configuration-typo
Fix Typo and Update Comment for Ollama Configuration
2023-10-31 11:14:51 -07:00
29a8cf8357 fix typo for ollama 2023-10-31 20:38:27 +07:00
7b6a6c7164 Fix formatting when last commit message contains _ 2023-10-31 10:05:13 +02:00
cf4d007737 Fix commands list for push trigger 2023-10-31 00:00:48 +02:00
a751bb0ef0 Merge pull request #414 from Codium-ai/ok/fix_github_bug
Bugfix: ignored github_app.commands on .pr-agent.toml
2023-10-30 20:28:54 +02:00
26d6280a20 Merge remote-tracking branch 'origin/main' into ok/fix_github_bug 2023-10-30 20:19:41 +02:00
32a19fdab6 Merge pull request #413 from Codium-ai/ok/bitbucket_repo_settings
Update Method to Fetch Repository Settings in bitbucket_provider.py
2023-10-30 20:18:59 +02:00
775ccb3f25 Refactor _perform_commands function in github_app.py to improve command handling 2023-10-30 20:14:25 +02:00
a1c6c57f7b Merge remote-tracking branch 'origin/main' into ok/bitbucket_repo_settings 2023-10-30 18:38:08 +02:00
73bb70fef4 Update get_repo_settings in bitbucket_provider.py to fetch file via API request 2023-10-30 18:36:46 +02:00
dcac6c145c Merge pull request #412 from Codium-ai/ok/fix_gitlab_bug
Update get_repo_settings to decode file from target branch in gitlab_…
2023-10-30 17:04:09 +02:00
4bda9dfe04 Update get_repo_settings to decode file from target branch in gitlab_provider.py 2023-10-30 17:01:49 +02:00
66644f0224 Merge pull request #411 from Codium-ai/ok/fix_gitlab_bug
Add Logging Context to Handle Request Calls in gitlab_webhook.py
2023-10-30 16:53:40 +02:00
e74bb80668 Refactor get_repo_settings method in gitlab_provider.py to decode file contents 2023-10-30 16:45:47 +02:00
e06fb534d3 Merge remote-tracking branch 'origin/main' into ok/fix_gitlab_bug 2023-10-30 16:34:03 +02:00
71a341855e Add log_context to handle_request calls in gitlab_webhook.py 2023-10-30 16:00:09 +02:00
7d949ad6e2 Update GENERATE_CUSTOM_LABELS.md 2023-10-30 15:20:17 +02:00
4b5f86fcf0 Merge pull request #410 from Codium-ai/fix_link_in_install
small fix in link in install.md
2023-10-30 02:58:52 -07:00
cd11f51df0 small fix in link in install.md 2023-10-30 11:47:24 +02:00
b40c0b9b23 Merge pull request #409 from zmeir/patch-1
Fix call to `_get_previous_review_comment`
2023-10-30 01:28:12 -07:00
816ddeeb9e Fix call to _get_previous_review_comment
Hey @mrT23, I thinks there's a problem with moving this line to after `self.git_provider.publish_comment(pr_comment)`.

The reason I originally placed it here is because otherwise, if you run `/review --pr_reviewer.remove_previous_review_comment=true` it will publish your review and then immediately after delete it, because it will look for the previous review comment only after you published your new review - so it will take your new review as the previous one. In order to get the real "previous" review you must collect the comments list before publishing a review, so placing this method call first ensures that.

The method `self._get_previous_review_comment()` is a no-op if `pr_reviewer.remove_previous_review_comment=false` so I see no downside in keeping it before `self.git_provider.publish_comment(pr_comment)`

Additionally, the check for `if previous_review_comment:` is redundant because it's done internally in `self._remove_previous_review_comment`. I thought it looked cleaner without this extra nesting here, but if you think more verbosity is better I'll keep it.
2023-10-30 09:06:51 +02:00
11f01a226c Update RELEASE_NOTES.md 2023-10-30 08:22:36 +02:00
b57ec301e8 Merge pull request #408 from Codium-ai/tr/final_fixes
fixed review
2023-10-29 09:02:48 -07:00
71da20ea7e better link 2023-10-29 18:01:50 +02:00
c895657310 fixed review 2023-10-29 17:59:46 +02:00
eda20ccca9 Merge pull request #407 from zmeir/patch-1
Update Usage.md with new GitHub App features
2023-10-29 16:54:59 +02:00
aed113cd79 Update Usage.md with new GitHub App features 2023-10-29 16:33:38 +02:00
0ab07a46c6 Merge pull request #405 from Codium-ai/tr/final_fixes
Final Fixes and Updates to PR Agent
2023-10-29 06:02:34 -07:00
5f32e28933 generate_labels 2023-10-29 15:02:16 +02:00
7538c4dd2f generate_labels 2023-10-29 14:59:50 +02:00
e3845283f8 release notes 2023-10-29 14:58:36 +02:00
a85921d3c5 release notes 2023-10-29 14:49:35 +02:00
27b64fbcaf release notes 2023-10-29 14:47:46 +02:00
8d50f2ae82 release notes 2023-10-29 14:43:45 +02:00
e97a03f522 Merge remote-tracking branch 'origin/main' into tr/final_fixes 2023-10-29 14:38:33 +02:00
2e3344b5b0 Merge pull request #406 from Codium-ai/hl/custom_labels
Add documentation to custom labels
2023-10-29 05:38:11 -07:00
e1b51eace7 release notes 2023-10-29 14:37:04 +02:00
49e3d5ec5f Add documentation 2023-10-29 13:58:01 +02:00
afa78ed3fb final fixes 2023-10-29 13:07:22 +02:00
72d5e4748e final fixes 2023-10-29 13:05:15 +02:00
61d3e1ebf4 Merge pull request #394 from zmeir/zmeir-external-push_trigger
Added support for automatic review on push event
2023-10-29 13:04:33 +02:00
055b5ea700 final fixes 2023-10-29 13:03:12 +02:00
3434296792 Documentation 2023-10-29 13:02:07 +02:00
ae375c2ff0 final fixes 2023-10-29 13:01:55 +02:00
3d5efdf4f3 Merge commit '9a585de36461a6941cb77009e5ab5f4b568a1ff7' into hl/custom_labels 2023-10-29 13:01:53 +02:00
9a585de364 Merge pull request #404 from Codium-ai/tr/final_fixes
final fixes
2023-10-29 12:31:40 +02:00
c27dc436c4 final fixes 2023-10-29 12:29:14 +02:00
e83747300d Merge branch 'main' of github.com:Codium-ai/pr-agent into hl/custom_labels 2023-10-29 12:09:43 +02:00
7374243d0b enable_custom_labels 2023-10-29 11:40:36 +02:00
5c568bc0c5 Merge pull request #403 from Codium-ai/tr/fix_custom_labels
Refactoring Custom Labels Handling and Documentation Update
2023-10-29 02:34:18 -07:00
22c196cb3b Merge remote-tracking branch 'origin/main' into tr/fix_custom_labels
# Conflicts:
#	pr_agent/git_providers/github_provider.py
2023-10-29 10:58:42 +02:00
d2cc856cfc Merge pull request #402 from Codium-ai/tr/github_action_uses_toml
Update GitHub Action to Use .pr_agent.toml
2023-10-29 01:55:33 -07:00
013a689b33 generate_labels fix 2023-10-29 10:43:04 +02:00
d772213cfc fix labels 2023-10-29 08:58:12 +02:00
638db96311 github action now also uses .pr_agent.toml 2023-10-28 13:34:32 +03:00
4dffabf397 Merge pull request #396 from Codium-ai/hl/custom_labels
Implement Custom Labels for PRs
2023-10-28 01:37:54 +03:00
6f2bbd3baa Add documentation 2023-10-28 00:45:59 +03:00
9e41f3780c disable custom labels by default 2023-10-27 21:22:56 +03:00
f53ec1d0cc move enable custom labels to custom labels function 2023-10-27 21:12:58 +03:00
f7666cb59a Update INSTALL.md 2023-10-27 11:49:39 +03:00
a7cb59ca8b small fix 2023-10-27 08:10:29 +03:00
ca0ea77415 refactor 2023-10-27 07:58:42 +03:00
0cf27e5fee custom labels disabled by default 2023-10-27 07:54:59 +03:00
f3bdbfc103 Add /generate_labels function + fix issues 2023-10-26 23:28:33 +03:00
20e3acdd86 Merge pull request #393 from Kryslynn93/patch-1
Update configuration.toml
2023-10-26 07:43:00 -07:00
f965b09571 Merge pull request #398 from Codium-ai/tr/readme_updates
Update Documentation and Installation Instructions
2023-10-26 07:37:05 -07:00
e6bea76eee Typo 2023-10-26 17:07:16 +03:00
414f2b6767 Fix incremental review if there are no new commits (would have performed a full review instead) 2023-10-26 16:49:55 +03:00
6541575a0e Refactor to use pull_request synchronize event 2023-10-26 16:49:54 +03:00
02570ea797 Remove previous review comment on push event 2023-10-26 16:46:54 +03:00
b8583c998d readme 2023-10-26 12:16:58 +03:00
726594600b readme 2023-10-26 12:10:14 +03:00
c77cc1d6ed readme 2023-10-26 11:56:03 +03:00
b6c9e01a59 readme 2023-10-26 11:51:32 +03:00
ec673214c8 Update INSTALL.md 2023-10-26 11:18:07 +03:00
16777a5334 Add custom label description 2023-10-25 13:48:27 +03:00
65bb70a1dd Added support for automatic review on push event
The new feature can be enabled via the new configuration `github_app.handle_push_event`. To avoid any unwanted side-effects, the current default of this configuration is set to `false`.

The high level flow (assuming the configuration is enabled):
1. receive push event from GitHub
2. extract branch and commits from event
3. find PR url for branch (currently does not support PRs from forks)
4. perform configured commands (e.g. `/describe`, `/review -i`)

The push event flow is guarded by a backlog queue so that multiple push events on the same branch won't trigger multiple duplicate runs of the PR-Agent commands.
Example timeline:
1. push 1 - start handling event
2. push 2 - waiting to be handled while push 1 event is still running
3. push 3 - event is dropped since handling it and handling push 2 is the same, so it is redundant
4. push 1 finished being handled
5. push 2 awakens from wait and continues handling (potentially reviewing the commits of both push 2 and push 3)

All of these options are configurable and can be enabled/disabled as per the user's desire.

Additional minor changes in this PR:
1. Created `DefaultDictWithTimeout` utility class to avoid too much boilerplate code in managing caches for outdated triggers.
2. Guard against running increment review when there are no new commits.
3. Minor styling changes for incremented review text.
2023-10-25 11:15:23 +03:00
1a89c7eadf refactor + add description options 2023-10-24 22:28:57 +03:00
07617eab5a add custom labels 2023-10-24 22:06:27 +03:00
f9e4c2b098 Update configuration.toml 2023-10-23 21:34:12 -04:00
fa24413201 Custom Labels 2023-10-23 16:29:33 +03:00
b6cabda586 quick fix 2023-10-19 17:24:37 +03:00
abbce60f18 Merge remote-tracking branch 'origin/main' 2023-10-19 17:10:30 +03:00
5daaaf2c1d quick fix 2023-10-19 17:10:21 +03:00
e8f207691e Merge pull request #391 from Codium-ai/tr/readme
Update and Enhance DESCRIBE.md Documentation
2023-10-19 02:03:50 -07:00
b0dce4ceae describe 2023-10-19 12:02:12 +03:00
fc494296d7 Merge pull request #387 from Codium-ai/ok/json_logging_in_bitbucket
Enhancing Logging in Bitbucket, GitLab, and Google Cloud Storage Secret Provider
2023-10-19 11:59:26 +03:00
67b4069540 describe 2023-10-19 11:45:41 +03:00
e6defcc846 describe 2023-10-19 11:43:18 +03:00
096fcbbc17 describe 2023-10-19 11:40:01 +03:00
eb7add1c77 describe 2023-10-19 11:38:21 +03:00
1b6fb3ea53 Merge pull request #385 from Codium-ai/hl/fix_add_docs_in_scripts
Add Blacklist for Non-Editable File Extensions in Documentation
2023-10-19 11:21:36 +03:00
c57b70f1d4 Merge pull request #390 from Codium-ai/tr/readme
Enhancing Documentation and Updating Configuration for PR Descriptions
2023-10-19 01:04:24 -07:00
a2c3db463a use_bullet_points 2023-10-19 10:45:42 +03:00
193da1c356 update readme 2023-10-19 09:22:26 +03:00
5bc26880b3 update readme 2023-10-19 09:20:36 +03:00
21a1cc970e - update readme
- minor prompts change
2023-10-19 09:16:20 +03:00
954727ad67 Merge pull request #386 from Codium-ai/ok/fix_bitbucket_pipeline
Refactor Bitbucket Pipeline Integration and Update Documentation
2023-10-18 16:45:26 +03:00
1314898cbf Enhance logging in bitbucket_app, gitlab_webhook, and google_cloud_storage_secret_provider with JSON format and additional context 2023-10-18 16:44:03 +03:00
ff04d459d7 Update Bitbucket Pipeline instructions in INSTALL.md, remove redundant functionality 2023-10-18 15:46:43 +03:00
88ca501c0c Merge pull request #377 from zmeir/zmeir-review_incremental_detect_header
Get previous incremental review
2023-10-18 00:30:42 +03:00
fe284a8f91 Merge pull request #382 from Codium-ai/tr/similar_issue_fix
Enhancements and Error Handling in Similar Issue Tool
2023-10-17 09:49:35 -07:00
d41fe0cf79 comment 2023-10-17 19:45:04 +03:00
3673924fe9 Add docs editable blacklist of file extensions like sql, yaml... 2023-10-17 18:50:39 +03:00
d5c098de73 another protection 2023-10-17 10:21:05 +03:00
9f5c0daa8e protection 2023-10-17 09:43:48 +03:00
bce2262d4e Merge pull request #381 from moccajoghurt/feature-allow-custom-urls
Support Custom Domain URLs for Azure DevOps Integration
2023-10-16 22:38:27 -07:00
e6f1e0520a remove azure.com url restriction 2023-10-16 20:38:14 +02:00
d8de89ae33 Get previous incremental review
When getting the last commit in `/review -i` consider also the last __incremental__ review, not just the last __full__ review

Full disclosure I'm not really sure the `/review -i` feature work very well - I might be wrong but it seemed like the actual review in fact addressed all the changes in the PR, and not just the ones from the last review (even though it adds a link to the commit of the last review).  
I think the commit list gathered in `/review -i` doesn't propagate the actual list the reviewer uses. Again, I might be wrong, just took a brief glance at it.
2023-10-16 16:37:10 +03:00
428c38e3d9 Merge pull request #376 from Codium-ai/feature/better_logger
Refactor logging system to use custom logger across the codebase
2023-10-16 16:32:27 +03:00
7ffdf8de37 Remove verbosity level check in handle_patch_deletions test 2023-10-16 16:25:57 +03:00
83e670c5df Enhance logging context in github_app server with server type 2023-10-16 16:13:09 +03:00
c324d88be3 Refactor logging system to use custom logger across the codebase 2023-10-16 14:56:00 +03:00
41166dc271 Merge pull request #374 from Codium-ai/fix/repo_specfic_settings
Repo settings file - fix a bug and apply to GitHub app
2023-10-13 15:44:05 -07:00
e7258e732b Refactor repo-specific settings application into a utility function, fix merge bug 2023-10-14 01:39:05 +03:00
18ee9d66b0 Update Usage.md 2023-10-13 05:00:41 +03:00
8755f635b4 Update Usage.md 2023-10-10 17:23:58 +03:00
e2417ebe88 Merge pull request #363 from Codium-ai/tr/pr_suggestions
publish each suggestion seperatly only on gitlab
2023-10-08 17:01:44 +03:00
264dea2a8b Merge pull request #359 from pzarfos/codecommit_changelog
added checkmark on features table for update_changelog with CodeCommit
2023-10-08 17:00:59 +03:00
da98fd712f note 2023-10-08 16:58:22 +03:00
e6548f4fe1 simpler solution 2023-10-08 16:57:22 +03:00
e66fed2468 Merge pull request #362 from zmeir/zmeir-fix_help_message_for_bot
Fixed help message for bot user
2023-10-08 16:52:18 +03:00
1b3fb49f9c publish each suggestion seperatly only on gitlab 2023-10-08 16:50:25 +03:00
66a5f06b45 Merge pull request #360 from zmeir/zmeir-fix_typo_in_describe_md
Small typo in DESCRIBE.md
2023-10-08 16:37:51 +03:00
51c817ba29 Merge pull request #357 from jamesrom/feat/file_ignores
Add support for ignoring files
2023-10-08 16:30:02 +03:00
8f9f09ecbf Fixed help message for bot user
This changes the help message to display properly when running a custom deployment of the PR-Agent app (i.e. not via GitHub Actions, and with the setting `github_app.override_deployment_type=false`)
2023-10-08 16:19:11 +03:00
d77a71bf47 Small typo in DESCRIBE.md 2023-10-08 14:35:54 +03:00
70c0ef5ce1 added checkmark on features table for update_changelog with CodeCommit 2023-10-07 09:57:51 -04:00
92e9012fb6 Error handling 2023-10-07 09:39:53 +11:00
43dc648b05 Simplify filter 2023-10-06 22:44:29 +11:00
6dee18b24a Update usage documentation 2023-10-06 22:13:03 +11:00
baa0e95227 Code comments for ignore.toml 2023-10-06 21:53:10 +11:00
b27f57d05d Update settings, documentation 2023-10-06 21:03:36 +11:00
fd8c90041c azure 2023-10-06 08:31:31 +03:00
ea6253e2e8 revert azure 2023-10-06 08:12:11 +03:00
2a270886ea Merge pull request #354 from Codium-ai/tr/prompts
Enhancements in Markdown Conversion and Line Number Linking
2023-10-05 18:27:29 +03:00
2945c36899 source_branch 2023-10-05 18:21:52 +03:00
1bab26f1c5 gfm_supported 2023-10-05 18:08:02 +03:00
72eecbbf61 add line number 2023-10-05 17:59:08 +03:00
989c56220b add line number 2023-10-05 17:48:36 +03:00
e387086890 Add support for ignoring files
Add ignore.toml, configuration for ignoring files
Add file_filter.py, for matching files against glob/regex patterns
Update relevant code to use file filter
+Tests
2023-10-06 01:43:35 +11:00
088f256415 stable 2023-10-05 17:03:10 +03:00
d92f5284df Merge pull request #352 from Codium-ai/tr/prompts
Refactoring Patch Extra Lines and Updating Prompts
2023-10-05 08:55:14 +03:00
f3e794e50b Patch Extra Lines 2023-10-05 08:46:02 +03:00
44239f1a79 Patch Extra Lines 2023-10-05 08:38:43 +03:00
428e6382bd prompts minor update 2023-10-05 08:17:37 +03:00
d13a92515b Merge pull request #349 from Codium-ai/tr/large_prs_readme
readme updates
2023-10-02 18:20:41 +03:00
eaf7cfbcf2 readme updates 2023-10-02 18:18:15 +03:00
abb633db0f readme updates 2023-10-02 18:10:41 +03:00
ca11cfa54e Merge pull request #347 from Codium-ai/hl/document_incremental_review
Add Incremental Review to user guide
2023-10-02 14:17:04 +03:00
8df57941c6 Merge pull request #348 from Codium-ai/add-x-badge
Update README.md - add X badge
2023-10-02 13:21:49 +03:00
af45dcc7df Update README.md - add X badge 2023-10-02 11:59:56 +03:00
18c33ae6fc add screenshot 2023-10-02 11:45:05 +03:00
896d65a43a Update README.md 2023-10-02 10:59:03 +03:00
02ea2c7a40 Merge pull request #345 from brianpham93/azure-devops-get-item-error
Azure Devops: Set file content as empty string when azure_devops_client.get_item fails
2023-10-02 10:57:08 +03:00
fdbb7f176c Merge pull request #346 from Codium-ai/GadiZimerman-patch-1
Update README.md
2023-10-02 10:56:46 +03:00
5386ec359d Merge pull request #337 from Codium-ai/hl/add_docs
Add code documentations in PR
2023-10-02 10:55:36 +03:00
85f64ad895 update readme and user guide 2023-10-02 10:11:28 +03:00
589d329a3c Add error logs for troubleshooting 2023-10-02 05:37:27 +00:00
3585a4ebff Update README.md 2023-10-01 23:20:13 +03:00
706f6bf44d update Roadmap 2023-10-01 19:57:34 +03:00
54f29fcf38 add Incremental Review to user guide 2023-10-01 19:55:41 +03:00
e941fa9ec0 Add to user tools guide 2023-10-01 19:51:15 +03:00
4479c5f11b Azure Devops: Set file content as empty string when error 2023-10-01 16:22:37 +00:00
b2369c66d8 Merge commit '8d075b76ae081d0d38813f789478e4fa0f404cd8' into hl/add_docs
# Conflicts:
#	README.md
2023-10-01 13:55:50 +03:00
ab5ac8ffa8 rename vars + Add to README.md 2023-10-01 13:52:00 +03:00
ccc7f1e10a rename vars 2023-10-01 13:07:08 +03:00
8d075b76ae Merge pull request #344 from brianpham93/azure-devops-ignore-tree
Ignore change item with `gitObjectType == 'tree'`
2023-10-01 10:39:03 +03:00
32a8b0e9bc Ignore change item with gitObjectType == 'tree' 2023-10-01 07:34:01 +00:00
69c6acf89d Merge pull request #343 from Codium-ai/tr/prompts_and_readme
Update Documentation and Prompts
2023-10-01 09:05:16 +03:00
e07412c098 prompt and readme updates 2023-10-01 09:00:58 +03:00
8cec3ffde3 Merge pull request #341 from Codium-ai/tr/readme
Update and Refactor Documentation
2023-09-30 10:54:58 +03:00
25bc54785b update docs 2023-09-30 10:51:37 +03:00
8b033ccc94 Merge pull request #340 from Codium-ai/tr/readme
Update Documentation and CLI Execution Method
2023-09-29 16:43:10 +03:00
2003b6915b typos 2023-09-29 16:39:25 +03:00
175218c779 cli 2023-09-29 16:31:13 +03:00
f96c6cbc95 Merge pull request #339 from Codium-ai/tr/readme
Enhancements to PR-Agent Documentation and Settings
2023-09-29 11:56:02 +03:00
4dbb70c9d5 typos 2023-09-29 11:54:37 +03:00
73cc7c2d71 typos 2023-09-29 11:53:39 +03:00
902975b660 Merge remote-tracking branch 'origin/tr/readme' into tr/readme 2023-09-29 11:44:19 +03:00
813fa8571e tools guide 2023-09-29 11:44:03 +03:00
b5c505f727 ASK 2023-09-29 11:43:38 +03:00
26b9e8a235 ASK 2023-09-29 11:37:31 +03:00
c90e72cb75 Improve 2023-09-29 11:30:47 +03:00
55f022d93e improve 2023-09-29 11:24:26 +03:00
71b532d4d5 <kbd> 2023-09-29 10:38:01 +03:00
c18ae77299 <kbd> 2023-09-29 10:36:58 +03:00
2f0fa246c0 borders 2023-09-29 10:34:18 +03:00
a371f7ab84 review 2023-09-29 10:30:20 +03:00
e1149862b2 similar issue 2023-09-29 10:01:32 +03:00
b8f93516ce describe 2023-09-29 09:47:13 +03:00
d15d374cdc Merge pull request #338 from coka-stefan/main
Add lockb to bad extensions
2023-09-29 09:20:49 +03:00
cae0f627e2 Add lockb to bad extensions 2023-09-28 22:26:25 +02:00
7fbdc3aead rstrip 2023-09-28 22:47:26 +03:00
0551922839 Merge commit '663ae92bdf3bb3a22b8b7ab437385c882f96e475' into hl/add_docs
# Conflicts:
#	pr_agent/tools/pr_add_docs.py
2023-09-28 22:46:07 +03:00
043d453cab add doc placement before after 2023-09-28 22:44:15 +03:00
663ae92bdf Add Docs 2023-09-28 22:42:03 +03:00
96824aa9e2 Revert "Add Docs"
This reverts commit 5cca299b16.
2023-09-28 21:16:14 +03:00
5cca299b16 Add Docs 2023-09-28 21:13:48 +03:00
cd3527f7d4 add configurable docstring style 2023-09-28 20:58:37 +03:00
bb12c75431 reformat 2023-09-28 20:15:18 +03:00
4accddcaa7 revert verbosity 2023-09-28 20:12:36 +03:00
bb8a0f10f4 refine /add_docs 2023-09-28 20:11:18 +03:00
c3cbaaf09e Initial add docs 2023-09-27 16:48:17 +03:00
a6e65e867f release notes v0.8 2023-09-27 09:23:19 +03:00
0df8071673 Merge pull request #331 from dulalbert/patch-2
INSTALL.md Fixed typo in the hyperlink
2023-09-26 08:22:07 +03:00
b17a4d9551 INSTALL.md Fixed typo in the hyperlink 2023-09-26 10:29:26 +08:00
dc7db4cbdd Merge pull request #330 from Codium-ai/tr/action_defaults
Enhancements in GitHub Action and Error Handling
2023-09-25 18:59:57 +03:00
4e94fcc372 typo 2023-09-25 18:57:50 +03:00
4c72cfbff4 auto tools in github action 2023-09-25 18:56:10 +03:00
ac89867ac7 Merge pull request #329 from Codium-ai/tr/action_defaults
Adding Environment Variables for Automated Actions in GitHub
2023-09-25 18:30:53 +03:00
34ed598c20 yaml 2023-09-25 18:30:20 +03:00
e7aee84ea8 isinstance 2023-09-25 18:23:56 +03:00
388684e2e8 none 2023-09-25 18:19:35 +03:00
8f81c18647 auto commands in github action 2023-09-25 18:01:32 +03:00
ba78475944 Merge pull request #327 from Codium-ai/tr/no_comments
Update Review Guidelines to Exclude Comments
2023-09-25 17:25:31 +03:00
75dd5688fa Merge pull request #324 from pzarfos/feature/get_pr_id_320
Added 'get_pr_id' function for the CodeCommit provider
2023-09-25 17:24:21 +03:00
aa32024078 no comments 2023-09-25 16:58:08 +03:00
9167c20512 added unit tests 2023-09-23 10:41:53 -04:00
a7fb5d98b1 add get_pr_id() to CodeCommitProvider 2023-09-23 08:08:46 -04:00
fda47bb5cf Merge pull request #304 from sarbjitsinghgrewal/fix_bitbucket_pipeline
Fix bitbucket pipeline
2023-09-22 14:36:42 +03:00
9c4f849066 Merge pull request #321 from dulalbert/patch-1
Correct typo in pr_reviewer_prompts.toml
2023-09-22 12:44:09 +03:00
3e2e2d6c6e update install.md 2023-09-22 12:23:41 +05:30
56cc804fcf Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_pipeline 2023-09-22 12:19:49 +05:30
62746294e3 Correct typo in pr_reviewer_prompts.toml 2023-09-22 10:23:01 +08:00
d384b0644e Merge pull request #320 from Codium-ai/tr/pr_id
Implementing get_pr_id method in Git Providers
2023-09-21 21:38:49 +03:00
3e07fe618f pr_id gitlab 2023-09-21 21:35:00 +03:00
be54fb5bf8 pr_id 2023-09-21 21:29:41 +03:00
46ec3c0754 implement suggestions in bitbucket pipeline 2023-09-21 14:42:59 +05:30
5e608cc7e7 Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_pipeline 2023-09-21 12:48:32 +05:30
04162564ca Merge pull request #315 from Codium-ai/tr/fixes_20_9
Fixing potential null values in git patch processing
2023-09-20 17:00:59 +03:00
992f51a019 protections 2023-09-20 15:59:35 +03:00
2bc25b7435 Merge remote-tracking branch 'origin/main' into tr/fixes_20_9 2023-09-20 15:58:05 +03:00
fcd9821d10 protections 2023-09-20 15:57:06 +03:00
911ad299e2 Merge pull request #314 from Codium-ai/mrT23-patch-1
Update README.md
2023-09-20 14:50:53 +03:00
fbfa186733 Update README.md 2023-09-20 14:50:21 +03:00
7545b25823 Update README.md 2023-09-20 14:49:50 +03:00
1370a051f1 Update INSTALL.md 2023-09-20 14:47:52 +03:00
dcbd3132d1 Release notes 2023-09-20 14:41:24 +03:00
497f84b3bd Update release notes 2023-09-20 14:23:55 +03:00
c2fe2fc657 Added a release notes file 2023-09-20 13:50:10 +03:00
f7abdc6ae8 Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_pipeline 2023-09-20 14:47:21 +05:30
d327245edf Merge pull request #312 from Codium-ai/tr/fixes_20_9
Enhancing error handling in PR review tools
2023-09-20 07:59:02 +03:00
632de3f186 protections 2023-09-20 07:39:56 +03:00
de14b0e4c0 Merge pull request #310 from never-known-soldier/update/README.md
Update README.md file
2023-09-19 13:29:35 +03:00
f010d1389b Merge branch 'main' into update/README.md 2023-09-19 13:28:39 +03:00
4411f6d88a Update README.md file 2023-09-19 12:54:33 +05:30
a2ca43afcd Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_pipeline 2023-09-18 10:34:18 +05:30
1f62520606 Merge pull request #306 from Codium-ai/tr/etr
Adding Estimated Review Effort Feature and Handling Cases with No Detected Language
2023-09-17 17:10:52 +03:00
c0511c954e icon 2023-09-17 17:08:02 +03:00
818ab5a9e8 fixed tests 2023-09-17 16:56:23 +03:00
291ffdd6ae gfm_markdown 2023-09-17 16:51:16 +03:00
4fbe7d14b5 protection for no language 2023-09-17 16:41:53 +03:00
ea91a38541 Estimated effort to review 2023-09-17 16:31:58 +03:00
caaee4e43d Estimated time to review 2023-09-15 17:09:58 +03:00
43af4aa182 remove token from config 2023-09-15 16:08:13 +05:30
e343ce8468 bitbucket pipeline for adding reviews 2023-09-15 16:05:55 +05:30
7b2c01181b Merge pull request #3 from sarbjitsinghgrewal/fix_bitbucket_pipeline
Fix bitbucket pipeline
2023-09-15 12:44:14 +05:30
978c56c128 update pipeline 2023-09-15 12:38:34 +05:30
4043dfff9e Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_pipeline 2023-09-14 15:31:18 +05:30
279d45996f Merge pull request #301 from Codium-ai/add-links-to-readme
Addition of Relevant Links to README.md
2023-09-14 12:38:57 +03:00
01aa038ad6 Update README.md - add links 2023-09-14 11:53:47 +03:00
084256b923 fixed config 2023-09-14 08:23:34 +03:00
dc42713217 Merge pull request #285 from Codium-ai/markers
Markers
2023-09-14 08:21:35 +03:00
99f17666c5 merge 2023-09-14 08:20:36 +03:00
bba22667f1 merge 2023-09-14 08:13:00 +03:00
1b8349b0ef merge 2023-09-14 07:47:04 +03:00
b94e3521d1 Merge remote-tracking branch 'origin/main' into markers
# Conflicts:
#	pr_agent/tools/pr_description.py
2023-09-14 07:46:30 +03:00
32931f0bc0 Update Usage.md 2023-09-13 10:38:31 +03:00
72ac8e8091 Merge pull request #299 from Codium-ai/tr/readme
Enhancement of Similar Issue Tool and Documentation Updates
2023-09-13 08:19:13 +03:00
33045e6898 graphic adjustments 2023-09-13 08:17:13 +03:00
069c3a8e5c Merge remote-tracking branch 'origin/tr/readme' into tr/readme 2023-09-13 08:16:36 +03:00
9c0656c296 graphic adjustments 2023-09-13 08:16:23 +03:00
228ee26541 graphic adjustments 2023-09-13 08:16:08 +03:00
f8d548367f graphic adjustments 2023-09-13 08:15:15 +03:00
d3f466f59b graphic adjustments 2023-09-13 08:04:36 +03:00
6b45940128 Merge remote-tracking branch 'origin/main' into tr/readme 2023-09-13 07:47:21 +03:00
a52e94fcbc similar issue 2023-09-13 07:46:43 +03:00
ee3874f0aa Merge pull request #297 from Codium-ai/tr/fix_tests
Enhancing Logging in pr_similar_issue.py
2023-09-13 07:27:50 +03:00
31ba7acf49 Support issue comments in GitHub Actions 2023-09-12 16:53:54 +03:00
b7a2551cab Support issue comments in GitHub Actions 2023-09-12 16:46:02 +03:00
d4eb100cbc Support issue comments in GitHub Actions 2023-09-12 16:44:20 +03:00
21feb92b75 Support issue comments in GitHub Actions 2023-09-12 16:41:12 +03:00
2f6178306f Fix a bug in GitHub Actions 2023-09-12 13:28:35 +03:00
36e7c1c22f Merge remote-tracking branch 'origin/main' 2023-09-12 13:24:55 +03:00
c31baa5aea Fix a bug in GitHub Actions 2023-09-12 13:24:47 +03:00
67052aa714 add bitbucket access token 2023-09-12 11:28:36 +05:30
caee7cbf50 add bitbucket access token 2023-09-12 11:20:38 +05:30
9bee3055c2 add bitbucket access token 2023-09-12 11:15:10 +05:30
901eda2f10 logs 2023-09-12 07:57:21 +03:00
8cf7d2d0b1 Merge pull request #296 from Codium-ai/tr/fix_tests
gfm_supported
2023-09-12 07:49:19 +03:00
d7f43d6ee0 gfm_supported 2023-09-12 07:43:15 +03:00
9bd5140ea4 update access token 2023-09-11 16:33:45 +05:30
12bd9e8b42 add bitbucket pipeline 2023-09-11 16:08:23 +05:30
ca8997b616 Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_publish_description 2023-09-11 09:36:23 +05:30
8e42162b5e Merge pull request #278 from sarbjitsinghgrewal/fix_bitbucket_publish_description
Fix bitbucket publish description
2023-09-10 14:13:26 +03:00
98d0835c48 Merge remote-tracking branch 'origin/main' into fix_bitbucket_publish_description 2023-09-10 14:08:17 +03:00
2aef9dfe55 Merge remote-tracking branch 'origin/main' into fix_bitbucket_publish_description 2023-09-10 14:06:54 +03:00
115b513c9b Remove 'bitbucket' explicit dependency anywhere that's not in bitbucket_provider.py 2023-09-10 14:06:13 +03:00
fd63fe4c95 Merge pull request #293 from Codium-ai/tr/litellm_debugger
Integration of Litellm Client with AI Handler
2023-09-10 13:56:06 +03:00
d40285e4d3 Merge branch 'main' into tr/litellm_debugger 2023-09-10 13:40:35 +03:00
517658fb37 Merge pull request #282 from Codium-ai/tr/issue_tool
Adding Similar Issue Tool and Pinecone Integration
2023-09-10 13:39:34 +03:00
f9f0f220c2 pinecone-datasets 2023-09-10 13:31:36 +03:00
6382b8a68b LITELLM_TOKEN 2023-09-10 13:28:56 +03:00
e371b217ec Merge remote-tracking branch 'origin/main' into tr/litellm_debugger 2023-09-10 13:27:19 +03:00
7dec7b0583 Merge pull request #291 from krrishdholakia/main
adding documentation on how to call local hf models
2023-09-10 13:25:44 +03:00
bf6a235add pinecone-datasets==0.6.1 2023-09-10 13:16:05 +03:00
1d9489c734 Merge remote-tracking branch 'origin/tr/issue_tool' into tr/issue_tool 2023-09-10 08:39:20 +03:00
bd588b4509 solved dependencies 2023-09-10 08:39:03 +03:00
245f29e58a solved dependencies 2023-09-10 08:22:42 +03:00
7f5f2d2d1a solved dependencies 2023-09-10 08:07:39 +03:00
fe500845b7 upgrade pip 2023-09-10 07:46:51 +03:00
b42b2536b5 upgrade pip 2023-09-10 07:39:01 +03:00
498ad3d19c upgrade pip 2023-09-10 07:36:25 +03:00
892dbe458e litellm client 2023-09-09 17:35:45 +03:00
1b098aea13 adding documentation on how to call local hf models 2023-09-08 09:59:44 -07:00
ed1816a2d7 Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_publish_description 2023-09-08 11:24:05 +05:30
e90c9e5853 Merge pull request #287 from cloudlinux/gerrit
[gerrit] Added support project's config file: `.pr_agent.toml`
2023-09-07 19:06:32 +03:00
e4f28b157f Added support project's config file: .pr_agent.toml
+ removed markdown/html formatting from the review due to gerrit does not support it
2023-09-07 13:13:07 +01:00
6fb8a882af ordering requirements.txt 2023-09-07 12:41:31 +03:00
9889d26d3e merged main 2023-09-07 12:31:22 +03:00
b23a4c0535 Merge remote-tracking branch 'origin/main' into tr/issue_tool
# Conflicts:
#	requirements.txt
2023-09-07 12:30:16 +03:00
0f7a481eaa Merge pull request #277 from krrishdholakia/hf-usage
Showing how to use huggingface models with PR-Agent
2023-09-07 12:28:47 +03:00
3fc88b2bc4 Merge pull request #276 from krrishdholakia/main
Add docs on using Azure
2023-09-07 12:20:26 +03:00
ed5aaaab45 Merge branch 'main' into main 2023-09-07 12:19:59 +03:00
145b5db458 added 'publish_description_as_comment' support 2023-09-07 12:10:33 +03:00
8321792a8d == 2023-09-06 18:12:16 +03:00
8af8fd8e5d github action 2023-09-06 17:43:43 +03:00
753ea3e44c Update INSTALL.md 2023-09-06 11:35:41 +03:00
660601f7c5 Merge pull request #280 from Codium-ai/coditamar/install
Update INSTALL.md
2023-09-06 11:32:18 +03:00
4e7f67f596 Merge pull request #279 from Codium-ai/coditamar/readme-minor-enhancement
Enhancements to README.md for Improved Clarity and Detail
2023-09-06 11:31:55 +03:00
e486addb8f Update INSTALL.md
minor clarification to Method 8 GitLab
2023-09-06 11:28:19 +03:00
4a5310e2a1 Update README.md 2023-09-06 10:46:25 +03:00
8962c9cf8a stable 2023-09-06 09:43:23 +03:00
bc95cf5b8e stable 2023-09-06 09:12:25 +03:00
dcd8196b94 Merge remote-tracking branch 'origin/main' into tr/issue_tool
# Conflicts:
#	pr_agent/settings/configuration.toml
2023-09-06 08:43:41 +03:00
901c1dc3f0 issue tool 2023-09-06 08:43:01 +03:00
adb9964823 Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_publish_description 2023-09-06 09:32:43 +05:30
335877c4a7 fix publish description for bitbucket 2023-09-06 09:26:23 +05:30
5da6a0147c showing how to use huggingface models 2023-09-05 16:23:22 -07:00
cd1ae55f4f bump litellm version to fix azure deployment id error 2023-09-05 15:26:45 -07:00
ca50724952 adding details on calling azure 2023-09-05 15:19:56 -07:00
460b315b53 Add Gitlab webhook installation documentation 2023-09-05 18:34:19 +03:00
00ff516e8a Merge pull request #264 from Codium-ai/ok/gitlab_webhook
Implementing Gitlab Webhook Secret Verification
2023-09-05 18:33:03 +03:00
55b3c3fe5c Add Gitlab webhook installation documentation 2023-09-05 18:31:29 +03:00
1443df7227 Merge remote-tracking branch 'origin/main' into ok/gitlab_webhook 2023-09-05 18:15:51 +03:00
739b63f73b Merge pull request #272 from cloudlinux/gerrit
Gerrit support
2023-09-05 13:54:57 +03:00
4a54532b6a Set github provider by default
- merge README.md
2023-09-05 09:09:35 +01:00
0dbe64e401 Merge branch 'main' into gerrit 2023-09-05 09:04:32 +01:00
c0b23e1091 Merge remote-tracking branch 'origin/main' into tr/issue_tool
# Conflicts:
#	pr_agent/algo/utils.py
2023-09-05 08:05:33 +03:00
704c169181 Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_improve_issue 2023-09-05 10:00:07 +05:30
746140b26e Add support for markers in description 2023-09-04 12:11:39 -04:00
53ce609266 Msg is mandatory field for the request 2023-09-04 15:50:36 +01:00
7584ec84ce Update README.md 2023-09-04 15:48:18 +01:00
140760c517 Add Gitlab webhook secret 2023-09-04 16:39:31 +03:00
56e9493f7a Add Gitlab webhook secret 2023-09-04 15:29:21 +03:00
958ecf333a Merge pull request #271 from pzarfos/codecommit_implemented_improve_cli
implement 'improve' command for CodeCommit
2023-09-04 07:56:23 +03:00
ae3d7067d3 implemented 'improve' command for CodeCommit 2023-09-03 09:22:08 -04:00
a49e81d959 Merge pull request #270 from Codium-ai/tr/readme_updates
Tr/readme updates
2023-09-03 11:43:23 +03:00
916d7c236e update README.md 2023-09-03 11:42:11 +03:00
6343d35616 Merge pull request #269 from pzarfos/remove_extra_swift
Removed duplicate swift statement
2023-09-03 10:35:27 +03:00
0203086aac removed duplicate swift statement 2023-09-02 15:39:57 -04:00
0066156aca Merge pull request #268 from Codium-ai/tr/readme_updates
Update Documentation and Reorder Installation Methods
2023-09-02 09:44:56 +03:00
544bac7010 update README.md 2023-09-02 09:43:46 +03:00
34090b078b Merge remote-tracking branch 'origin/main' into tr/readme_updates 2023-09-02 09:37:57 +03:00
9567199bb2 update README.md 2023-09-02 09:37:44 +03:00
1f7a833a54 update README.md 2023-09-02 09:33:33 +03:00
990f69a95d update README.md 2023-09-02 09:25:38 +03:00
2b8a8ce824 update README.md 2023-09-02 09:19:35 +03:00
6585854c85 Merge pull request #267 from Codium-ai/tr/readme_updates
Update Documentation and README
2023-09-01 20:28:09 +03:00
98019fe97f update CONFIGURATION.md 2023-09-01 20:27:29 +03:00
d52c11b907 update CONFIGURATION.md 2023-09-01 20:26:36 +03:00
e79bcbed93 update CONFIGURATION.md 2023-09-01 20:25:07 +03:00
690c819479 update CONFIGURATION.md 2023-09-01 20:22:10 +03:00
630d1d9e03 Merge remote-tracking branch 'origin/tr/readme_updates' into tr/readme_updates
# Conflicts:
#	CONFIGURATION.md
2023-09-01 20:14:56 +03:00
20c32375e1 auto 2023-09-01 20:14:14 +03:00
44b790567b update readme 2023-09-01 20:10:23 +03:00
4d6d6c4812 update 2023-09-01 20:09:53 +03:00
7f6493009c update 2023-09-01 20:05:33 +03:00
7a6efbcb55 update 2023-09-01 19:56:20 +03:00
777c773a90 update 2023-09-01 19:50:10 +03:00
f7c698ff54 update 2023-09-01 19:40:38 +03:00
1b780c0496 Merge pull request #265 from szecsip/feature_azure_devops_docs
Add docs and dependencies for Azure DevOps provider
2023-09-01 19:32:23 +03:00
2e095807b7 Update README.md
Added information about features of gerrit provider
2023-09-01 15:05:48 +01:00
ae98cfe17b Return status of upload suggestion to the gerrit 2023-09-01 14:56:16 +01:00
35a6eb2e52 Add unique patch to according to multiple execution of publish_code_suggestions 2023-09-01 14:51:29 +01:00
8b477c694c Fix after rebase on main branch 2023-09-01 14:41:56 +01:00
1254ad1727 Gerrit:
- support reflect/answer
2023-09-01 14:31:35 +01:00
eeea38dab3 Gerrit support 2023-09-01 14:31:34 +01:00
8983fd9071 Merge pull request #261 from Codium-ai/tr/github_app
Update Configuration Documentation
2023-08-31 20:43:58 +03:00
918ae25654 Merge remote-tracking branch 'origin/main' into tr/github_app 2023-08-31 20:36:56 +03:00
de39595522 Merge pull request #252 from zmeir/zmeir-try_to_fix_split_line_ranges
Try to refine the prompt to prevent split line ranges that give bad suggestions
2023-08-31 20:36:33 +03:00
4c6595148b Add Gitlab webhook secret 2023-08-31 17:03:58 +03:00
02e0f958e7 Add Gitlab webhook secret 2023-08-31 14:56:45 +03:00
be19b64542 add dependencies for Azure DevOps provider 2023-08-31 11:53:54 +00:00
24900305d6 update docs for Azure DevOps 2023-08-31 11:50:41 +00:00
06d00032df update docs for Azure DevOps 2023-08-31 11:47:51 +00:00
244cbbd27f Merge pull request #262 from idavidov/idavidov/add_reference
Idavidov/add reference
2023-08-31 13:24:59 +03:00
970a7896e9 Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_improve_issue 2023-08-31 13:35:32 +05:30
8263bf5f9c small refactor of azure devops 2023-08-31 09:26:16 +03:00
8823d8c0e9 small format change 2023-08-31 09:21:40 +03:00
5cbcef276c cross link in INSTALL for GitAPP configuration overwrite 2023-08-31 09:19:02 +03:00
ce9014073c Editing the prompts 2023-08-31 08:19:33 +03:00
376c4523dd Editing the prompts 2023-08-31 08:08:09 +03:00
e0ca594a69 Editing the prompts 2023-08-31 08:06:43 +03:00
48233fde23 Editing the prompts 2023-08-31 08:02:14 +03:00
9c05a6b1b5 Merge pull request #258 from Codium-ai/tr/github_app
Update GitHub App Configuration Instructions
2023-08-31 02:17:36 +03:00
da848d7e39 Merge pull request #260 from zmeir/zmeir-fix_describe_user_description
Fix #254
2023-08-31 02:16:55 +03:00
c6c97ac98a Try to change the improve command prompt to prevent split lines range 2023-08-30 23:33:38 +03:00
92e23ff260 Fix #254 2023-08-30 23:05:41 +03:00
aa03654ffc Merge pull request #259 from krrishdholakia/patch-3
bump litellm version - prevent default logging
2023-08-30 20:32:59 +03:00
85130c0d30 Merge pull request #255 from pzarfos/issue_138_codecommit_describe
Enhancement of AWS CodeCommit support in PR-Agent
2023-08-30 20:30:50 +03:00
3c27432f50 GitHub App instructions 2023-08-30 19:53:28 +03:00
eec62c14dc bump litellm version - prevent default logging 2023-08-30 09:49:01 -07:00
ad6dd38fe3 GitHub App instructions 2023-08-30 19:46:33 +03:00
307b3b4bf7 GitHub App instructions 2023-08-30 19:42:46 +03:00
8e7e13ab62 Merge pull request #253 from Codium-ai/coditamar/readme-minor
Enhancements to README.md
2023-08-30 18:45:59 +03:00
bd085e610a Merge pull request #256 from Codium-ai/ok/override_settings
Allow overriding GitHub app default action by using repo local file
2023-08-30 18:44:45 +03:00
d64b1f80da Allow overriding GitHub app default action by using repo local file 2023-08-30 12:12:09 +03:00
f26264daf1 added describe command to CodeCommit 2023-08-29 17:59:52 -04:00
2aaa722102 Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_improve_issue 2023-08-29 09:49:19 +05:30
edaeb99b43 Update README.md
type

Co-authored-by: Tim Perkins <tjwp@users.noreply.github.com>
2023-08-28 22:29:08 +03:00
ce54a7b79e Update README.md 2023-08-28 22:08:08 +03:00
f14c5d296a Merge pull request #251 from zmeir/zmeir-fix_azure_api
Fixed incorrect usage for Azure OpenAI API
2023-08-28 20:52:04 +03:00
18d46fb655 Merge pull request #250 from Codium-ai/tr/prompts_yaml
Refactor Code to Use YAML Instead of JSON for PR Code Suggestions
2023-08-28 20:25:31 +03:00
07bd926678 Merge pull request #249 from Codium-ai/tr/readme_updates
Enhancements to Installation Instructions and Readme
2023-08-28 20:22:41 +03:00
d3c7dcc407 AZURE_DEVOPS_AVAILABLE 2023-08-28 20:21:29 +03:00
f5dd7207dc Merge remote-tracking branch 'origin/main' into tr/prompts_yaml 2023-08-28 20:19:22 +03:00
e5e10d5ec5 Merge pull request #241 from szecsip/feature_azure_devops
Add Azure DevOps provider with basic functionality
2023-08-28 17:03:05 +03:00
314d13e25f Fixed incorrect usage for Azure OpenAI API 2023-08-28 16:13:26 +03:00
2dc2a45e4b yaml 2023-08-28 09:48:43 +03:00
39522abc03 fix conflicts 2023-08-28 11:21:47 +05:30
3051dc50fb update README.md 2023-08-28 08:41:02 +03:00
e776cebc33 update README.md 2023-08-28 08:31:56 +03:00
33ef23289f Merge pull request #248 from Codium-ai/ok/requirements
Consolidation of Redundant Dependency Lists
2023-08-27 16:36:46 +03:00
85bc307186 Consolidate redundant dependency list 2023-08-27 16:00:38 +03:00
a0f53d23af Consolidate redundant dependency list 2023-08-27 15:58:14 +03:00
82ac9d447b Consolidate redundant dependency list 2023-08-27 15:39:45 +03:00
9286e61753 Consolidate redundant dependency list 2023-08-27 15:36:39 +03:00
56828f0170 Merge pull request #246 from Codium-ai/ok/bitbucket_server
Implementing Bitbucket Server Support
2023-08-27 10:27:00 +03:00
9e878d0d9a Bitbucket server 2023-08-27 10:11:46 +03:00
0e42634da4 add publish_labels and get_labels functions 2023-08-25 10:15:30 +05:30
b94ed61219 Merge branch 'main' into feature_azure_devops 2023-08-24 16:41:33 +00:00
ceaff2a269 fix exception printing 2023-08-24 16:35:34 +00:00
12167bc3a1 fix imports 2023-08-24 16:34:20 +00:00
355abfc39a Bitbucket server, WIP 2023-08-24 18:35:41 +03:00
c163d47a63 fix imports 2023-08-24 15:22:14 +00:00
5d529a71ad some minor changes in Azure DevOps git provider 2023-08-24 15:20:00 +00:00
5079daa4ad Bitbucket server, WIP 2023-08-24 16:33:51 +03:00
f0dc485305 Merge branch 'main' of https://github.com/Codium-ai/pr-agent into fix_bitbucket_improve_issue 2023-08-24 16:14:29 +05:30
db6bf41051 update readme 2023-08-24 15:56:20 +05:30
123741faf3 Bitbucket server, WIP 2023-08-24 12:10:13 +03:00
67ff50583a fix improve, update_changelog and review inline comment 2023-08-24 11:52:20 +05:30
01d1cf98f4 init Azure DevOps git provider 2023-08-23 16:01:10 +00:00
52ba2793cd modify get_main_pr_language to handle azuredevops provided language format 2023-08-23 15:59:49 +00:00
fd39c64bed Merge pull request #233 from zmeir/zmeir-automatic_github_app_options
Support custom deployments for github_app.py and add more options for automatic review actions
2023-08-23 09:20:46 +03:00
49c58f997a Merge pull request #234 from Codium-ai/tr/tweaks_and_improvements
Refactoring PR Reviewer's Settings and Markdown Conversion
2023-08-23 09:19:56 +03:00
16150e9c84 update litellm 2023-08-23 09:19:15 +03:00
6599cbc7f2 Merge remote-tracking branch 'origin/main' into tr/tweaks_and_improvements 2023-08-23 09:17:53 +03:00
2dfad0bb20 Merge pull request #228 from krrishdholakia/main
adding huggingface inference support + litellm debugger
2023-08-23 09:17:33 +03:00
53108a9b20 Merge branch 'main' into main 2023-08-23 09:16:00 +03:00
f2ab623e76 Merge pull request #235 from pzarfos/issue_138_codecommit_pr_2
Initial implementation of CodeCommit (second attempt)
2023-08-23 00:39:50 +03:00
3a93dcd6a7 Add build and test on pull request open, reopen 2023-08-23 00:37:04 +03:00
d31b66b656 initial implementation of CodeCommit 2023-08-22 17:15:11 -04:00
f17b4fcc9e Made the automatic describe command the least destructive 2023-08-22 21:14:03 +03:00
5582a901ff Merge branch 'main' into zmeir-automatic_github_app_options 2023-08-22 21:09:23 +03:00
412c86593d fixed tests 2023-08-22 20:21:52 +03:00
04be1573d5 improved review 2023-08-22 20:10:36 +03:00
3d771e28ce Remove redundant None default in dict.get 2023-08-22 18:33:25 +03:00
a9a7a55f02 Remove redundant toggle 2023-08-22 18:28:43 +03:00
62fe1de12d Remove redundant toggle 2023-08-22 18:28:06 +03:00
4184f81090 Merge pull request #231 from idavidov/idavidov/gihub-action-doc-permissions
adding permissions to the GitHub Actions section of INSTALL
2023-08-22 17:00:25 +03:00
635b243280 Merge pull request #223 from zmeir/zmeir-keep_original_pr_description
Enhancement: Retain Original User Description and Title in PRs
2023-08-22 16:47:16 +03:00
cbe0a695d8 Merge pull request #229 from Codium-ai/tr/sequential_improve
Implementing Extended Improve Mode for More Thorough PR Reviews
2023-08-22 16:46:52 +03:00
782c170883 Support custom deployments for github_app.py and add more options for automatic review actions 2023-08-22 16:46:03 +03:00
9157fa670e -> bool 2023-08-22 16:32:22 +03:00
36e5e5a17e update 2023-08-22 16:30:18 +03:00
f4f040bf8d publish each suggestion separably 2023-08-22 16:11:51 +03:00
82fb611a26 Add options to keep original user title 2023-08-22 10:32:58 +03:00
580af44e7d Could we consider adding permissions to the GitHub Actions section? I've noticed that this has been a point of confusion for some users, as evidenced by questions in our Discord channel and GitHub issues. Some folks may even be discouraged to the point of not seeking help. I believe adding permissions could significantly improve the user experience. What are your thoughts? 2023-08-22 10:24:20 +03:00
09ef809080 Added comments explaining the logic behind get_user_description 2023-08-22 10:04:21 +03:00
2b22f712fb Renamed keep_user_description --> add_original_user_description 2023-08-22 09:55:56 +03:00
b85679e5e4 improve --extend 2023-08-22 09:42:59 +03:00
dcad490513 adding huggingface inference support + litellm debugger 2023-08-21 15:31:51 -07:00
fb9335f424 extended improve 2023-08-21 18:17:34 +03:00
81c38f9646 Added type hints 2023-08-21 09:22:58 +03:00
b1a2e3e323 Merge pull request #224 from pzarfos/hotfix/remove_duplicate_get_repo_settings
Remove duplicate get_repo_settings() in bitbucket_provider
2023-08-21 08:19:19 +03:00
542bc9586a Remove duplicate get_repo_settings() in bitbucket_provider 2023-08-20 12:58:44 -04:00
b3749d08e2 Set default configuration to false to allow users to opt-in 2023-08-20 19:00:56 +03:00
31e91edebc Allow keeping the original user description 2023-08-20 18:59:40 +03:00
6693aa3cbc semi stable 2023-08-20 15:01:06 +03:00
fda98643c2 Merge pull request #217 from sarbjitsinghgrewal/fix_bitbucket_improve_issue
fix bitbucket improve issue
2023-08-20 14:42:15 +03:00
2bbb25d59c Merge pull request #222 from Codium-ai/ok/fix_gitlab
Fix repo settings bug in Gitlab
2023-08-20 14:41:31 +03:00
08afeb9759 Merge pull request #219 from idavidov/idavidov/gitlab_bug
Fixing GitLab Inline Comment Diff Issue by Implementing Relevant Diff Selection
2023-08-20 14:40:32 +03:00
2d5b0fa37f Fix repo settings bug in Gitlab 2023-08-20 14:39:05 +03:00
99f5a2ab0f Merge pull request #216 from idavidov/idavidov/specific_version
Adding Instructions for Specific Docker Image Version and GitHub Action Commit in Installation Guide
2023-08-20 14:12:29 +03:00
d7dcecfe00 Merge pull request #220 from zmeir/zmeir-safe_parse_config_overrides
Safe parse key value in config override
2023-08-20 10:29:53 +03:00
c6f8d985c2 Safe parse key value in config override 2023-08-20 10:11:39 +03:00
532dfd223e Merge pull request #215 from tjwp/auto-review
Addition of Automatic Review Configuration for GitHub App
2023-08-19 16:50:34 +03:00
9770f4709a few more changes suggested by AI implemented 2023-08-19 16:26:15 +03:00
35afe758e9 revert back conf 2023-08-19 16:16:16 +03:00
50125ae57f various changes as outcomes from AI review 2023-08-19 16:12:48 +03:00
6595c3e0c9 2 more /improve good suggestions 2023-08-19 15:47:45 +03:00
fdd16f6c75 raize exception when no diffs in MR 2023-08-19 15:40:40 +03:00
7b7e913195 to changes suggested by /improve with my small touch 2023-08-19 15:31:02 +03:00
5477469a91 in order to have exact sha's we have to find correct diff for this change
otherwise gitlab web doesn't able show diff on page and return 500 or 400 errors based on different scenarios
2023-08-19 15:06:22 +03:00
dff4646920 fix bitbucket improve issue 2023-08-18 17:48:45 +05:30
6e7622822e Merge branch 'main' into idavidov/specific_version 2023-08-18 10:04:23 +03:00
631fb93b28 Implement Automatic Review Configuration for GitHub app 2023-08-16 16:24:30 -04:00
dee1f168f8 Merge pull request #206 from pzarfos/fix/bitbucket_get_repo_settings
Implement get_repo_settings for BitbucketProvider
2023-08-16 18:54:36 +03:00
bb18e32c56 Merge pull request #209 from tjwp/no-suggestions
Publish comment when improve has no suggestions
2023-08-16 18:53:39 +03:00
7803d8eec4 + pin to specific commit with gihub actions in Istallation part of readme 2023-08-16 14:22:14 +03:00
9a84b4b184 + digest usage for docker in Istallation part of readme 2023-08-16 12:56:15 +03:00
70286e9574 Make the message more modest 2023-08-15 08:35:57 -04:00
3f60d12a9a Publish comment when improve has no suggestions 2023-08-14 13:07:00 -04:00
164b340c29 Merge branch 'main' into fix/bitbucket_get_repo_settings 2023-08-14 08:30:14 -04:00
4bb035ec0f Merge pull request #208 from sarbjitsinghgrewal/fix_bitbucket_ask_issue
Fix bitbucket ask issue
2023-08-14 14:52:45 +03:00
23a79bc8fe Merge pull request #183 from zmeir/zmeir-fallback_deployments
Support fallback deployments to accompany fallback models
2023-08-14 14:51:14 +03:00
1db53ae1ad update readme file 2023-08-14 14:45:25 +05:30
cca951d787 fix bitbucket ask issue 2023-08-14 14:30:30 +05:30
230d684cd3 Merge pull request #202 from zmeir/zmeir-remove_blank_line
Remove extra blank line in help message
2023-08-14 07:12:50 +03:00
0a02fa8597 Merge pull request #203 from tjwp/tjwp/contents-permission
Contents read-only permission needed by /review -i
2023-08-14 07:12:38 +03:00
f82b9620af Implement get_repo_settings for BitbucketProvider 2023-08-13 18:25:11 -04:00
524faadffb init AzureDevopsProvider 2023-08-13 23:00:45 +02:00
82710c2d15 add AzureDevopsProvider to __init__.py 2023-08-13 22:56:50 +02:00
ce29d9eb49 Contents read-only permission needed by /review -i 2023-08-13 07:28:05 -04:00
b7b650eb05 Remove extra blank line in help message 2023-08-13 11:32:02 +03:00
6ca0655517 Extracted to helper functions 2023-08-13 11:03:10 +03:00
edcf89a456 Improve comment 2023-08-13 10:56:16 +03:00
7762a67250 Fail if not enough fallback deployments 2023-08-13 10:55:44 +03:00
7049c73790 Merge branch 'main' into zmeir-fallback_deployments 2023-08-13 10:48:21 +03:00
cc7be0811a Merge pull request #200 from Codium-ai/tr/block_scalar
Block scalar format
2023-08-12 09:49:27 +03:00
d3a5aea89e update_changelog 2023-08-11 18:50:56 +03:00
dd87df49f5 block scalar 2023-08-11 18:43:46 +03:00
e85bcf3a17 Merge remote-tracking branch 'origin/tr/block_scalar' into tr/block_scalar 2023-08-11 18:38:06 +03:00
abb754b16b block scalar 2023-08-11 18:37:55 +03:00
bb5878c99a Merge branch 'main' into tr/block_scalar 2023-08-11 18:36:21 +03:00
273a9e35d9 block scalar 2023-08-11 18:35:34 +03:00
fcc208d09f Merge pull request #194 from Codium-ai/ok/test_action
Implementing GitHub Actions Workflow for Build and Test
2023-08-10 16:47:26 +03:00
20bbdac135 Test github action 2023-08-10 16:41:50 +03:00
ceedf2bf83 Merge branch 'main' into ok/test_action 2023-08-10 16:40:01 +03:00
2d6b947292 Test github action 2023-08-10 16:37:02 +03:00
2e13b12fe6 Merge pull request #193 from coditamar/fix/workflow_yaml_permissions
adding `permissions` to `review.yaml`, also adding some comments
2023-08-10 16:17:32 +03:00
2d56c88291 Merge remote-tracking branch 'upstream/main' into fix/workflow_yaml_permissions 2023-08-10 16:16:47 +03:00
cf9c6a872d Test github action 2023-08-10 16:09:29 +03:00
0bb8ab70a4 Merge remote-tracking branch 'origin/main' 2023-08-10 15:16:10 +03:00
4a47b78a90 Rename workflow 2023-08-10 15:16:03 +03:00
3e542cd88b adding permissions to review.yaml, also adding some comments 2023-08-10 08:10:10 +03:00
17ed050ca7 Merge pull request #192 from coditamar/fix/minor_cli_and_requirements_fixes
Correcting CLI and README Descriptions and Fixing Requirements.txt
2023-08-10 02:18:13 +03:00
e24c5e3501 Update requirements.txt 2023-08-10 02:16:16 +03:00
b206b1c5ff Protect for empty description 2023-08-10 02:08:36 +03:00
0270306d3c litellm was mentioned twice in the requirements.txt 2023-08-10 01:34:24 +03:00
3e09b9ac37 fixing pr_url param description (was wrongly mentioned as pr-url) 2023-08-10 01:31:06 +03:00
725ac9e85d fixing cli pr_url help description 2023-08-10 01:30:12 +03:00
e00500b90c PyYAML dependency 2023-08-10 00:56:28 +03:00
f1f271fa00 PyYAML dependency 2023-08-10 00:44:00 +03:00
d38c5236dd Merge pull request #187 from Codium-ai/ok/limit_description
Limiting Description and Commit Messages Length
2023-08-09 14:14:47 +03:00
49a3a1e511 Merge pull request #188 from Codium-ai/tr/update_review_prompt
Update PR Review and Description Generation to Use YAML
2023-08-09 14:14:36 +03:00
1b0b90e51d block scalar 2023-08-09 14:11:58 +03:00
64481e2d84 block scalar 2023-08-09 14:01:48 +03:00
e0f295659d A less hacky way 2023-08-09 12:17:54 +03:00
fe75e3f2ec yaml
yaml
2023-08-09 12:15:52 +03:00
e3274af831 A (still) hacky way to clip description and commit messages 2023-08-09 10:17:58 +03:00
95b6abef09 Merge branch 'main' into zmeir-fallback_deployments 2023-08-08 11:00:13 +03:00
7f1849a867 Logging 2023-08-07 22:42:53 +03:00
7760f37dee Merge pull request #185 from zmeir/zmeir-fix_inline_comment_position
Attempt to fix bug in create_inline_comment
2023-08-07 20:41:52 +03:00
ebbe655c40 Don't commment on Github, only eyes reaction 2023-08-07 18:09:39 +03:00
164ed77d72 Attempt to fix bug in create_inline_comment 2023-08-07 17:09:50 +03:00
b1148e5f7a Don't commment on Github, only eyes reaction 2023-08-07 16:34:28 +03:00
2012e25596 Merge pull request #182 from Codium-ai/ok/add_eyes_reaction
Add Eyes Reaction to Comments and Configure AI Timeout
2023-08-07 16:28:38 +03:00
a75253097b Don't remove eyes 2023-08-07 16:28:20 +03:00
079d62af56 Merge pull request #181 from Codium-ai/ok/inference_timeout
Configurable AI Timeout
2023-08-07 16:23:06 +03:00
6c4a5bae52 Support fallback deployments to accompany fallback models
This is useful for example in Azure OpenAI deployments where you have a different deployment per model, so the current fallback implementation doesn't work (still uses the same deployment for each fallback attempt)
2023-08-07 16:18:48 +03:00
886139c6b5 Support adding / removing reaction from comments in GitHub different servers 2023-08-07 16:18:08 +03:00
8f751f7371 Default timeout for AI is now 180s, configurable 2023-08-07 13:26:28 +03:00
43297b851f Merge pull request #177 from Codium-ai/tr/update_readme
Update README and CONFIGURATION Documentation
2023-08-07 09:26:12 +03:00
4f39239e73 readme update
readme update
2023-08-07 09:11:54 +03:00
00e1925927 Merge pull request #172 from krrishdholakia/patch-1
adding support for Anthropic, Cohere, Replicate, Azure
2023-08-06 18:38:36 +03:00
7189b3ab41 suggestions -> feedback 2023-08-06 18:20:39 +03:00
a00038fbd8 Merge remote-tracking branch 'origin/main' into patch-1 2023-08-06 18:09:09 +03:00
a45343793a Merge pull request #175 from Codium-ai/tr/review_adjustments
Making the 'Review' Feature Great Again
2023-08-06 12:14:43 +03:00
703215fe83 updating secrets template 2023-08-05 22:53:59 -07:00
0f975ccf4a bug fixes 2023-08-05 22:50:41 -07:00
7367c62cf9 TestFindLineNumberOfRelevantLineInFile 2023-08-06 08:31:15 +03:00
fed0ea349a find_line_number_of_relevant_line_in_file
find_line_number_of_relevant_line_in_file
2023-08-06 08:13:07 +03:00
bd86266a4b Merge pull request #173 from Codium-ai/tr/caching
Optimization of PR Diff Processing
2023-08-05 09:23:45 +03:00
bd07a0cd7f Update Configuration.md 2023-08-04 12:13:04 +03:00
ed8554699b bug fixes and updates 2023-08-03 16:05:46 -07:00
749ae1be79 Update CHANGELOG.md 2023-08-03 19:55:51 +00:00
0e3dbbd0f2 fix major bug in gitlab 2023-08-03 22:51:38 +03:00
7a57db5d88 load_large_diff is done once 2023-08-03 22:14:05 +03:00
102edcdcf1 adding support for Anthropic, Cohere, Replicate, Azure 2023-08-03 12:04:08 -07:00
c92648cbd5 caching 2023-08-03 21:38:18 +03:00
26b008565b Merge pull request #170 from Codium-ai/tr/edge_case_for_hunks
Handling edge case for hunks in git patch processing
2023-08-03 12:11:27 +03:00
0dec24aa37 edge case for hunks 2023-08-03 10:50:22 +03:00
68a2f2a27d fix requirement.txt 2023-08-03 10:19:51 +03:00
cfa14178f8 Merge pull request #168 from Codium-ai/tr/further_use_commit_messages
Use commit messages in PR tools
2023-08-03 07:58:25 +03:00
b97c4b6114 Update CHANGELOG.md 2023-08-02 18:36:34 +03:00
3d43cecbea Merge pull request #167 from zmeir/zmeir-list_configurations_as_comment
Add /config command to list the possible configuration settings
2023-08-02 18:35:20 +03:00
eb143ec851 Update CHANGELOG.md 2023-08-02 15:32:15 +00:00
3e94a71dcd commit_messages_str is used in all tools 2023-08-02 18:26:39 +03:00
dd14423b07 Add /config command to list the possible configuration settings 2023-08-02 16:42:54 +03:00
8e47fdc284 Merge pull request #164 from Codium-ai/ok/repo_config
Support for Repo-Specific Configuration File
2023-08-01 19:09:23 +03:00
ab607d74be Support repo-specific configuration file 2023-08-01 18:36:20 +03:00
bfe7304449 Support repo-specific configuration file 2023-08-01 18:04:52 +03:00
e12874b696 Support repo-specific configuration file 2023-08-01 17:44:08 +03:00
696e2bd6ff Support repo-specific configuration file 2023-08-01 17:27:25 +03:00
450f410e3c Support repo-specific configuration file 2023-08-01 17:22:03 +03:00
08a3f033cb Merge pull request #162 from Codium-ai/ok/settings_refactor
Refactor settings usage and CLI
2023-08-01 16:05:20 +03:00
c5a79ceedd Merge remote-tracking branch 'origin/main' into ok/settings_refactor 2023-08-01 16:01:04 +03:00
13547afc58 Merge pull request #163 from Codium-ai/tr/commit_messages
Adding Commit Messages Retrieval Functionality
2023-08-01 15:59:26 +03:00
8ae936e504 Bug fixes 2023-08-01 15:58:23 +03:00
e577d27f9b Update CHANGELOG.md 2023-08-01 12:38:31 +00:00
dfb73c963a get_commit_messages for gitlab 2023-08-01 15:30:14 +03:00
8c0370a166 Commit messages in pr-description 2023-08-01 15:15:59 +03:00
d7b77764c3 Support context aware settings (for each incoming request), support override of settings, refactor CLI to use pr_agent.py 2023-08-01 14:43:26 +03:00
6605f9c444 typos in 'commands_text' 2023-07-31 11:02:30 +03:00
2a8adcbbd6 update README.md 2023-07-30 22:16:56 +03:00
0b22c8d427 update README.md 2023-07-30 22:04:59 +03:00
dfa0d9fd43 update README.md 2023-07-30 22:01:14 +03:00
c8470645e2 add tests and update README.md 2023-07-30 21:54:07 +03:00
5a181e52d5 Merge pull request #159 from Codium-ai/tr/edit_any_config_setting
The Configurator Strikes Back
2023-07-30 15:19:07 +03:00
0ad8dcd2aa Merge remote-tracking branch 'origin/tr/edit_any_config_setting' into tr/edit_any_config_setting 2023-07-30 12:27:40 +03:00
e2d015a20c final 2023-07-30 12:27:32 +03:00
a0cfe4b48a Update CHANGELOG.md 2023-07-30 12:26:53 +03:00
a6ba8b614a Example args 2023-07-30 12:16:43 +03:00
4f0fabd2ca update_settings_from_args refactor 2023-07-30 12:14:26 +03:00
42b047a14e update_settings_from_args 2023-07-30 12:04:57 +03:00
3daf94954a update_settings_from_args 2023-07-30 11:43:44 +03:00
b564d8ac32 Merge pull request #147 from zmeir/zmeir-align_describe_styling
Minor improvements to describe command
2023-07-28 20:55:15 +03:00
d8e6da74db Update .dockerignore 2023-07-28 12:15:17 +03:00
278f1883fd Merge pull request #153 from marshally/fix_iteration_error_in_reflect_tmp
fix TypeError when iterating discussion_messages
2023-07-28 12:12:12 +03:00
ef71a7049e fix TypeError when iterating discussion_messages
When `pr-agent` is reviewing a long list of messages, a TypeError is thrown on the line

```python
for message in reversed(discussion_messages):
```

When reviewing the PyGithub library, the recommend an alternate syntax for iterating a paginated list in reverse.

https://github.com/PyGithub/PyGithub/blob/v1.59.0/github/PaginatedList.py#L122-L125

```
    If you want to iterate in reversed order, just do::

        for repo in user.get_repos().reversed:
            print(repo.name)
```

And here's a copy of the actual traceback

```
Traceback (most recent call last):
  File "/app/pr_agent/servers/github_action_runner.py", line 68, in <module>
    asyncio.run(run_action())
  File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/app/pr_agent/servers/github_action_runner.py", line 64, in run_action
    await PRAgent().handle_request(pr_url, body)
  File "/app/pr_agent/agent/pr_agent.py", line 19, in handle_request
    await PRReviewer(pr_url, is_answer=True).review()
  File "/app/pr_agent/tools/pr_reviewer.py", line 49, in __init__
    answer_str, question_str = self._get_user_answers()
  File "/app/pr_agent/tools/pr_reviewer.py", line 253, in _get_user_answers
    for message in reversed(discussion_messages):
TypeError: object of type 'PaginatedList' has no len()
```
2023-07-28 11:04:46 +02:00
6fde87b3bd Merge pull request #152 from Codium-ai/tr/gitlab_fixes
Improvements and Error Handling for GitLab Provider
2023-07-28 11:40:53 +03:00
07fe91e57b Update CHANGELOG.md 2023-07-28 08:39:42 +00:00
01e2f3f0cd Merge pull request #150 from Codium-ai/ok/handle_installation_id_properly
Github App: handle concurrent requests from multiple installations of app
2023-07-28 11:38:14 +03:00
63a703c000 Handle marketplace hook 2023-07-28 11:30:51 +03:00
4664d91844 bug fixes in gitlab code suggestion 2023-07-28 11:24:14 +03:00
8f16c46012 try-except 2023-07-28 10:52:49 +03:00
a8780f722d Handle marketplace hook 2023-07-28 03:22:25 +03:00
1a8fce1505 Updated handling of installation id 2023-07-28 02:44:28 +03:00
8519b106f9 Updated .gitignore 2023-07-28 02:28:50 +03:00
d375dd62fe Merge pull request #141 from patryk-kowalski-ds/pg/pip_package
Transition to pip package with pyproject.toml
2023-07-28 02:23:06 +03:00
3770bf8031 Update setup.py 2023-07-28 02:22:38 +03:00
5c527eca66 Merge remote-tracking branch 'origin/main' into pg/pip_package 2023-07-28 02:19:04 +03:00
b4ca52c7d8 updated Dockerfile.github_action 2023-07-28 02:18:12 +03:00
a78d741292 updated pyproject.toml 2023-07-28 02:09:01 +03:00
42388b1f8d Merge pull request #146 from idavidov/idsvidov/gitlabpaginator_fix
Fix for GitLab Paginator in GitLab Provider
2023-07-28 02:01:04 +03:00
0167003bbc handle no diffs 2023-07-28 01:59:10 +03:00
2ce91fbdf5 Merge pull request #148 from eltociear/patch-1
Fix typo in PR_COMPRESSION.md
2023-07-28 01:50:30 +03:00
aa7659d6bf Fix typo in PR_COMPRESSION.md
Withing -> Within
2023-07-28 00:18:58 +09:00
4aa54b9bd4 Add /describe -c option 2023-07-27 17:42:50 +03:00
c6d0bacc08 Match styling of both /describe modes 2023-07-27 17:31:31 +03:00
99ed9b22a1 latest documentation suggest get_all not all
https://python-gitlab.readthedocs.io/en/stable/api-usage.html#pagination
2023-07-27 15:39:19 +03:00
eee6d51b40 issue #145
get all diffs in merge request and not only gitlab default 20
2023-07-27 14:41:36 +03:00
a50e137bba Merge pull request #133 from idavidov/idavidov/github-ratelimit-message
Handling GitHub API Rate Limit Exceeded Exception
2023-07-27 14:22:11 +03:00
92c0522f4d Merge pull request #144 from Codium-ai/tr/readme_update
Update README with 'Why use PR-Agent?' section
2023-07-27 10:43:56 +03:00
6a72df2981 Merge pull request #139 from Codium-ai/tr/changelog
Add feature to update CHANGELOG.md based on PR content
2023-07-27 09:04:48 +03:00
808ca48605 if not self.commit_changelog: 2023-07-27 08:48:39 +03:00
c827cbc0ae final touches 2023-07-27 08:47:26 +03:00
48fcb46d4f Delete CHANGELOG.md 2023-07-27 08:46:14 +03:00
66b94599ec Update CHANGELOG.md 2023-07-27 08:45:33 +03:00
231efb33c1 add CHANGELOG.md 2023-07-27 08:43:29 +03:00
eb798dae6f Why use PR-Agent
Why use PR-Agent
2023-07-27 08:25:05 +03:00
52576c79b3 Update CHANGELOG.md 2023-07-26 20:40:28 +03:00
cce2a79a1f add CHANGELOG.md 2023-07-26 20:40:15 +03:00
413e5f6d77 general 2023-07-26 20:37:38 +03:00
09ca848d4c Merge remote-tracking branch 'origin/tr/changelog' into tr/changelog 2023-07-26 20:33:32 +03:00
801923789b final 2023-07-26 20:33:21 +03:00
cfb696dfd5 Delete CHANGELOG.md 2023-07-26 20:09:18 +03:00
2e7a0a88fa Update CHANGELOG.md 2023-07-26 20:08:29 +03:00
1dbbafc30a add CHANGELOG.md 2023-07-26 20:08:06 +03:00
d8eae7faab Delete CHANGELOG.md 2023-07-26 20:06:23 +03:00
14eceb6e61 PRUpdateChangelog 2023-07-26 20:05:18 +03:00
884317c4f7 stable 2023-07-26 20:03:22 +03:00
c5f4b229b8 Merge pull request #142 from patryk-kowalski-ds/pk/local-git-provider-impvs
Improvements to Local Git Provider
2023-07-26 19:18:35 +03:00
5a2a17ec25 Merge pull request #140 from Codium-ai/tr/enhance_review
Enhancement of PRReviewer class in pr_reviewer.py
2023-07-26 17:32:15 +03:00
1bd47b0d53 enhance pr_reviewer.py code 2023-07-26 17:24:03 +03:00
7531ccd31f stable 2023-07-26 16:29:42 +03:00
3b19827ae2 Add validation for repository path 2023-07-26 15:29:09 +02:00
ea6e1811c1 Fixed PR title - should be feature branch name, not target branch name 2023-07-26 14:15:50 +02:00
bc2cf75b76 Use pyproject.toml to install dependencies instead of requirements.txt. Fix incorrect mangum version 2023-07-26 09:14:24 +02:00
9e1e0766b7 Set python min version to 3.10 2023-07-26 09:13:54 +02:00
ccde68293f Update README.md 2023-07-26 10:09:01 +03:00
99d53af28d Update CHANGELOG.md 2023-07-26 09:50:21 +03:00
5ea607be58 Add package setup 2023-07-26 08:48:12 +02:00
e3846a480e s 2023-07-26 09:21:31 +03:00
a60a58794c Merge pull request #132 from Codium-ai/tr/code_enhancment
Enhancement of GitHub Webhook and Polling Server
2023-07-26 07:24:46 +03:00
8ae5faca53 Fix cyclic dependency 2023-07-25 16:52:18 +03:00
28d6adf62a Quick fix for github action 2023-07-25 16:41:29 +03:00
1229fba346 + settings.github.ratelimit_retries setup in configuration.toml 2023-07-25 16:37:13 +03:00
59a59ebf66 Quick fix for github action 2023-07-25 16:36:58 +03:00
36ab12c486 Merge pull request #136 from Codium-ai/ok/handle_sub_group
Handle subgroup in GitLab merge request URL parsing
2023-07-25 16:15:35 +03:00
0254e3d04a Merge pull request #128 from patryk-kowalski-ds/deepsense.ai/local-git-provider
Add Local Git Provider Support
2023-07-25 16:15:02 +03:00
f6036e936e + settings.github.ratelimit_retries setup in configuration.toml 2023-07-25 15:23:40 +03:00
10a07e497d Handle sub group in gitlab MR URLs 2023-07-25 15:15:51 +03:00
3b334805ee still need GithubException.RateLimitExceededException in pr_processing.py for correct exception catch 2023-07-25 15:14:56 +03:00
b6f6c903a0 moved @retry to github_provider.py and fetch number of retries from settings 2023-07-25 15:12:02 +03:00
55637a5620 added retry decorator similar to used in ai_handler following @okotek suggestion 2023-07-25 14:42:54 +03:00
404cc0a00e small change to show message and fail 2023-07-25 14:20:20 +03:00
0815e2024c - Replaced two dot diff with three dot diff. Cleaned up obsolete code linked to double dot diff.
- Moved target_branch_existence assertion to _prepare_repo method
- Renamed branch_name -> target_branch_name
- Simplified get_files method
2023-07-25 13:07:21 +02:00
41dcb75e8e Merge pull request #134 from Codium-ai/ok/gitlat_use_oauth
Use OAuth token for GitLab API
2023-07-25 14:04:50 +03:00
d23daf880f Change gitlab API to use oauth_token instead of PAT (PAT shuold work as well) 2023-07-25 13:58:48 +03:00
d1a8a610e9 Revert "show how much time until rate limit reset"
This reverts commit 8f482cd41a.
2023-07-25 13:38:55 +03:00
918549a4fc Implementing 'is_supported' method 2023-07-25 12:35:39 +02:00
8f482cd41a show how much time until rate limit reset
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-07-25 13:23:19 +03:00
34096059ff quick and dirty response for github API ratelimit, until some smart solution will be implemented 2023-07-25 13:05:56 +03:00
2dfbfec8c2 refactor 2023-07-24 19:48:24 +03:00
6170995665 replaced hardcoded main with actual target_branch name' 2023-07-24 16:59:07 +02:00
ca42a54bc3 Update pr_agent/git_providers/local_git_provider.py
Co-authored-by: Ori Kotek <orikotek@gmail.com>
2023-07-24 16:47:05 +02:00
c0610afe2a Update pr_agent/git_providers/local_git_provider.py
Co-authored-by: Ori Kotek <orikotek@gmail.com>
2023-07-24 16:46:46 +02:00
d4cbcc465c Update pr_agent/git_providers/local_git_provider.py
Co-authored-by: Ori Kotek <orikotek@gmail.com>
2023-07-24 16:46:36 +02:00
adb3f17258 Merge pull request #131 from Codium-ai/ok/gitlab_webook
GitLab Webhook Integration and Provider Enhancements
2023-07-24 16:01:17 +03:00
2c03a67312 Add labels 2023-07-24 16:00:51 +03:00
55eb741965 Merge pull request #125 from Codium-ai/tr/code_enhancment
Code Enhancement in PR Agent
2023-07-24 15:37:53 +03:00
8e6518f071 Added GitPython to requirements. Changed default review path (aesthetics) 2023-07-24 14:28:37 +02:00
c9c95d60d4 Implement gitlab webhook 2023-07-24 15:05:24 +03:00
02ecaa340f Local Git Provider Implementation 2023-07-24 12:49:57 +02:00
cca809e91c run_action 2023-07-24 12:45:24 +03:00
57ff46ecc1 stable 2023-07-24 12:41:00 +03:00
3819d52eb0 Merge remote-tracking branch 'origin/tr/code_enhancment' into tr/code_enhancment 2023-07-24 12:15:17 +03:00
3072325d2c PRDescription 2023-07-24 12:14:53 +03:00
abca2fdcb7 Merge remote-tracking branch 'origin/main' into tr/code_enhancment 2023-07-24 12:04:54 +03:00
4d84f76948 _get_prediction 2023-07-24 11:31:35 +03:00
dd8f6eb923 Merge pull request #126 from Codium-ai/ok/preserve_labels
Add functionality to preserve existing labels in PRs
2023-07-24 10:22:51 +03:00
b9c25e487a On /describe, preserve the current labels 2023-07-24 10:17:26 +03:00
1bf27c38a7 _prepare_pr_answer 2023-07-24 09:15:45 +03:00
1f987380ed Merge pull request #124 from Xyand/bugfix/mising_model
Bugfix - missing function argument
2023-07-24 07:36:21 +03:00
cd8bbbf889 bugfix 2023-07-24 00:58:21 +03:00
8e5498ee97 Merge pull request #122 from Codium-ai/update-readme-gifs-2
Update README.md
2023-07-23 17:40:26 +03:00
0412d7aca0 Update README.md 2023-07-23 17:38:08 +03:00
1eac3245d9 Merge pull request #121 from Codium-ai/update-gifs
Update GIF URLs in README
2023-07-23 17:33:47 +03:00
cd51bef7f7 Merge pull request #119 from zmeir/zmeir-code_suggestions_single_api_call
Optimize Code Suggestions API Calls
2023-07-23 17:30:37 +03:00
e8aa33fa0b Update README.md 2023-07-23 17:27:26 +03:00
54b021b02c Merge pull request #120 from Codium-ai/ok/remove_gitlab_polling
Temporarily remove gitlab polling server until a rewrite is ready
2023-07-23 17:07:59 +03:00
32151e3d9a Temporarily remove gitlab polling server until a rewrite is ready 2023-07-23 17:04:41 +03:00
32358678e6 Reduce the number of GitHub API calls when pushing code suggestions 2023-07-23 16:59:08 +03:00
42e32664a1 Merge pull request #118 from Codium-ai/ok/fallback_models
Handling exceptions in fallback models
2023-07-23 16:43:30 +03:00
1e97236a15 Add support for fallback models 2023-07-23 16:39:25 +03:00
321f7bce46 Merge pull request #117 from Codium-ai/ok/fallback_models
Implementing Fallback Models for Tokenization
2023-07-23 16:20:10 +03:00
02a1d8dbfc Add support for fallback models 2023-07-23 16:16:36 +03:00
e34f9d8d1c Merge pull request #116 from Codium-ai/fix-describe-gif
Fix describe gif
2023-07-23 14:18:21 +03:00
35dac012bd Update README.md 2023-07-23 14:17:27 +03:00
21ced18f50 Merge pull request #115 from Codium-ai/ok/readme_update
Update Installation Instructions in README
2023-07-23 13:42:06 +03:00
fca78cf395 Merge pull request #114 from Codium-ai/update-readme-gifs
Update README GIFs
2023-07-23 13:33:27 +03:00
d1b91b0ea3 Update INSTALL.md 2023-07-23 13:03:44 +03:00
76e00acbdb Update INSTALL.md 2023-07-23 13:02:32 +03:00
2f83e7738c Update README.md gifs 2023-07-23 12:06:04 +03:00
f4a226b0f7 Merge pull request #113 from Codium-ai/tr/cli_refactor
Refactoring CLI Commands Handling
2023-07-23 08:37:26 +03:00
f5e2838fc3 refactor 2023-07-21 22:12:51 +03:00
bbdfd2c3d4 Merge pull request #108 from patryk-kowalski-ds/deepsense.ai/configurable-language-extensions
Configurable Language Extensions
2023-07-21 21:47:52 +03:00
74572e1768 Update action.yaml 2023-07-20 22:02:08 +03:00
f0a17b863c Merge remote-tracking branch 'origin/main' 2023-07-20 22:00:24 +03:00
86fd84e113 Update action name 2023-07-20 22:00:17 +03:00
d5b9be23d3 Merge pull request #110 from linuxlewis/main
Fix TypeError for GitlabProvider
2023-07-20 19:52:43 +03:00
057bb3932f Merge pull request #109 from Codium-ai/mrT23-patch-1
Update README.md
2023-07-20 19:51:51 +03:00
05f29cc406 Fix TypeError for GitlabProvider 2023-07-20 11:49:42 -05:00
63c4c7e584 Merge pull request #90 from zmeir/zmeir-output_progress
Add Option to Control Comment Publishing Progress
2023-07-20 18:48:20 +03:00
1ea23cab96 Merge pull request #105 from Codium-ai/ok/retry_on_rate_limit_error
Retry on Rate Limit Error
2023-07-20 18:31:04 +03:00
e99f9fd59f Update README.md 2023-07-20 17:36:40 +03:00
fdf6a3e833 Merge pull request #107 from Codium-ai/mrT23-patch-1
Update README.md
2023-07-20 16:49:20 +03:00
79cb94b4c2 Add use_extra_bad_extensions to configuration.toml 2023-07-20 15:41:03 +02:00
9adec7cc10 README update 2023-07-20 16:40:14 +03:00
1f0df47b4d Update README.md 2023-07-20 16:39:28 +03:00
a71a12791b Move hard-coded language configurations to a configuration file.
Load this configuration file in the config loader.
2023-07-20 15:37:42 +02:00
23fa834721 Merge pull request #106 from Codium-ai/ok/readme_update
Update README.md
2023-07-20 15:35:12 +03:00
9f67d07156 README update 2023-07-20 15:32:16 +03:00
6731a7643e README update 2023-07-20 15:31:35 +03:00
f87fdd88ad README update 2023-07-20 15:29:46 +03:00
f825f6b90a README update 2023-07-20 15:29:46 +03:00
f5d5008a24 README update 2023-07-20 15:29:46 +03:00
0b63d4cde5 README update 2023-07-20 15:29:46 +03:00
2e246869d0 Retry on rate limit error on OpenAI calls 2023-07-20 15:02:34 +03:00
2f9546e144 Retry on rate limit error on OpenAI calls 2023-07-20 15:01:12 +03:00
6134c2ff61 Merge remote-tracking branch 'origin/main' 2023-07-20 14:56:24 +03:00
3cfbba74f8 Fix README 2023-07-20 14:56:14 +03:00
050bb60671 Merge pull request #103 from Codium-ai/mrT23-patch-1
Update README.md
2023-07-20 14:39:55 +03:00
12a7e1ce6e Update README.md 2023-07-20 12:09:58 +03:00
cd0438005b Merge pull request #101 from Codium-ai/tr/docstring
Adding Docstrings to Python Files
2023-07-20 11:53:17 +03:00
7c3188ae06 Merge pull request #102 from Codium-ai/hl/question_fix
Refactoring Question Handling in CLI
2023-07-20 11:18:00 +03:00
6cd38a37cd fix question cli 2023-07-20 11:10:34 +03:00
12e51bb6aa Merge remote-tracking branch 'origin/main' into tr/docstring 2023-07-20 10:54:53 +03:00
e2a4cd6b03 docstring 2023-07-20 10:51:21 +03:00
329e228aa2 Merge pull request #100 from Codium-ai/tr/describe_labels
Enhancement of Code Review Functionality
2023-07-19 21:32:28 +03:00
3d5d517f2a code suggestions 2023-07-19 20:57:14 +03:00
a2eb2e4dac Update pr_agent/git_providers/github_provider.py 2023-07-19 20:31:10 +03:00
d89792d379 PR Type label 2023-07-19 20:25:54 +03:00
23ed2553c4 Update README.md 2023-07-19 18:42:21 +03:00
fe29ce2911 Update README.md 2023-07-19 18:33:38 +03:00
df25a3ede2 typo 2023-07-19 18:22:26 +03:00
4c36fb4df2 args bug 2023-07-19 18:18:18 +03:00
67c61e0ac8 Update help 2023-07-19 17:27:12 +03:00
0985db4e36 Update help 2023-07-19 17:20:26 +03:00
ee2c00abeb split(" ") -> split() 2023-07-19 17:14:55 +03:00
577f24d107 Merge pull request #89 from zmeir/zmeir-review_score
Add Score Review Feature
2023-07-19 17:07:05 +03:00
fc24b34c2b Merge pull request #96 from Codium-ai/ok/update_readme
Update INSTALL.md with additional information on handling secrets
2023-07-19 17:05:12 +03:00
1e962476da Merge pull request #94 from Codium-ai/hl/incremental_review
Add Incremental Review /review -i
2023-07-19 17:02:38 +03:00
3326327572 More refactoring.... 2023-07-19 17:01:56 +03:00
36be79ea38 ignore merge from main 2023-07-19 16:14:59 +03:00
523839be7d Merge commit 'd1586ddd77b86f0d3b29aee3370f29624799e388' into hl/incremental_review 2023-07-19 16:14:33 +03:00
d1586ddd77 Merge pull request #97 from Codium-ai/mrT23-patch-1
Update utils.py
2023-07-19 15:32:41 +03:00
3420853923 Merge pull request #98 from Codium-ai/hl/update_readme
Update README.md
2023-07-19 15:32:06 +03:00
1f373d7b0a Update README.md 2023-07-19 15:31:29 +03:00
7fdbd6a680 Update utils.py 2023-07-19 15:12:50 +03:00
17b40a1fa1 Merge commit '7abbe08ff15d31c7e20de6d88638bfe27430c2f4' into hl/incremental_review 2023-07-19 14:24:44 +03:00
c47e74c5c7 Merge commit '5bc2ef1eff1f570779191a8f3c7a562f5b8fe230' into hl/incremental_review 2023-07-19 14:24:01 +03:00
7abbe08ff1 Merge pull request #95 from Codium-ai/tr/disable_review_suggestiosn
disable code suggestions by default for review
2023-07-19 14:23:54 +03:00
8038b6ab99 refactor and clean 2023-07-19 14:22:34 +03:00
6e26ad0966 Merge branch 'main' into zmeir-review_score 2023-07-19 13:37:55 +03:00
7e2449b228 Changed score type to int 2023-07-19 13:37:35 +03:00
97bfee47a3 minor fixes 2023-07-19 11:34:55 +03:00
3b27c834a4 Merge remote-tracking branch 'origin/main' into ok/update_readme 2023-07-19 11:14:44 +03:00
5bc2ef1eff Merge pull request #92 from YuviGold/deploy-on-lambda
Deployment on AWS Lambda
2023-07-19 11:12:29 +03:00
2f558006bf Update INSTALL.md, add notes about injecting secrets 2023-07-19 11:09:35 +03:00
8868c92141 Merge branch 'main' into zmeir-review_score 2023-07-19 11:05:24 +03:00
370520df51 Update docker/Dockerfile.lambda
have a fixed mangum version

Co-authored-by: Ori Kotek <orikotek@gmail.com>
2023-07-19 11:05:24 +03:00
e17dd66dce Disable score review by default 2023-07-19 11:00:28 +03:00
fc8494d696 Rephrase score description in promt 2023-07-19 10:59:52 +03:00
f8aea909b4 Add example output 2023-07-19 10:57:35 +03:00
2e832b8fb4 Merge pull request #86 from Codium-ai/GadiZimerman-patch-1
Update README.md
2023-07-19 10:51:31 +03:00
ccddbeccad num_code_suggestions=0 for review 2023-07-19 09:34:17 +03:00
a47fa342cb Merge pull request #88 from zmeir/zmeir-cli_args
CLI Arguments Refactoring
2023-07-19 08:15:19 +03:00
f73cddcb93 Change Review title when 2023-07-19 01:03:47 +03:00
5f36f0d753 Merge commit 'bdf7eff7cd0a8894c3e66e49bdf89f27da1bfcb4' into hl/incremental_review 2023-07-18 23:28:43 +03:00
dc4bf13d39 Add Incremental Review 2023-07-18 23:14:47 +03:00
bdf7eff7cd Merge pull request #87 from Codium-ai/tr/bug_fix
Add Insights from User's Answers and Fix User Answers Fetching
2023-07-18 18:20:15 +03:00
dc67e6a66e Support deploying pr-agent on AWS Lambda 2023-07-18 17:46:42 +03:00
6d91f44634 Added configuration option to control publishing review progress
This can be useful in a few situations:
1. To reduce the number of GitHub API calls (thus avoiding hitting the rate limit)
2. When the trigger for the agent is an external process (e.g. some external CI job), so there is no need to publish a message like "preparing review..." because it's not a part of a natual conversation with the user
2023-07-18 16:41:58 +03:00
0396e10706 Add configuration to request a score for the PR
This can help teams compare the review of the PR agent with that of a human reviewer, and fine-tune a score threshold for automatic approval where they decide the agent's review is satisfactory.
2023-07-18 16:40:35 +03:00
77f243b7ab Allow passing CLI args (helps with debugging) 2023-07-18 16:39:46 +03:00
c507785475 bugfix 2023-07-18 16:32:51 +03:00
5c5015b267 Update README.md 2023-07-18 14:45:15 +03:00
3efe08d619 Merge pull request #85 from Codium-ai/hl/always_filer_bad_extensions
Filter out bad files before getting their head and original source code and diff
2023-07-18 13:50:25 +03:00
2e36fce4eb Merge pull request #83 from Codium-ai/hl/gitlab_description
Support describe for Gitlab
2023-07-18 13:47:32 +03:00
d6d4427545 Merge pull request #84 from Codium-ai/GadiZimerman-patch-1
Update README.md
2023-07-18 13:37:43 +03:00
5d45632247 Performance improvement: Filter out bad files before getting their head and original source code and diff 2023-07-18 13:33:32 +03:00
90c045e3d0 Update README.md
changing image
2023-07-18 13:26:19 +03:00
7f0a96d8f7 readme 2023-07-18 13:17:30 +03:00
8fb9affef3 add try catch 2023-07-18 13:14:01 +03:00
6c42a471e1 Merge pull request #76 from zmeir/zmeir-publish_inline_comments_single_api_call
Optimization of Inline Comments Publishing
2023-07-18 13:05:11 +03:00
f2b74b6970 support gitlab describe function 2023-07-18 13:03:36 +03:00
ffd11aeffc Merge pull request #81 from Codium-ai/GadiZimerman-patch-1
Update README.md
2023-07-18 12:55:26 +03:00
05e4e09dfc Lint 2023-07-18 12:27:28 +03:00
13092118dc Move the new git provider function to the abstract interface 2023-07-18 12:26:49 +03:00
7d108992fc Merge remote-tracking branch 'origin/main' into zmeir-publish_inline_comments_single_api_call 2023-07-18 11:53:41 +03:00
e5a8ed205e Merge pull request #82 from Codium-ai/ok/lint
Linting and Code Cleanup
2023-07-18 11:40:43 +03:00
90f97b0226 Lint fixes 2023-07-18 11:34:57 +03:00
9e0f5f0ccc Merge pull request #78 from Codium-ai/tr/agent_logic
Enhancement of PR Agent with User Interaction
2023-07-18 10:37:08 +03:00
87ea0176b9 Update README.md 2023-07-18 10:36:05 +03:00
62f08f4ec4 removed an unneeded file 2023-07-18 10:35:05 +03:00
fe0058f25f Merge branch 'tr/agent_logic' of github.com:Codium-ai/pr-agent into tr/agent_logic 2023-07-18 10:34:40 +03:00
6d2673f39d Merge remote-tracking branch 'origin/tr/agent_logic' into tr/agent_logic 2023-07-18 10:32:43 +03:00
b3a1d456b2 if settings.pr_reviewer.num_code_suggestions 2023-07-18 10:32:36 +03:00
f77a5f6929 Call PRAgent from github_action_runner.py 2023-07-18 10:31:24 +03:00
fdeae9c209 Update pr_agent/agent/pr_agent.py 2023-07-18 10:20:52 +03:00
a994ec1427 Call PRAgent from github_action_runner.py 2023-07-18 10:19:32 +03:00
e5259e2f5c Small refactor 2023-07-18 10:17:09 +03:00
978348240b Update README.md 2023-07-18 09:59:47 +03:00
4d92e7d9c2 Update README.md
consider changing section headers to reflect commands format
2023-07-18 09:56:40 +03:00
6f1b418b25 Merge pull request #79 from patryk-kowalski-ds/deepsense.ai/gitlab-provider-file-creation-handling
Fixes 404 error on gitlab file provider happening in case a MR introduced a new file.
2023-07-18 08:27:59 +03:00
51e08c3c2b reflect and review + protections 2023-07-18 08:22:25 +03:00
4c29ff2db1 Merge remote-tracking branch 'origin/tr/agent_logic' into tr/agent_logic
# Conflicts:
#	pr_agent/tools/pr_description.py
2023-07-18 08:06:47 +03:00
5fbaa4366f publish_output instead publish_review 2023-07-18 08:05:42 +03:00
aee08ebbfe Merge branch 'main' into tr/agent_logic 2023-07-18 08:04:47 +03:00
6ad8df6be7 Merge pull request #80 from Codium-ai/ok/remove_pics
Remove most pics from repo
2023-07-17 23:51:24 +03:00
539edcad3c works 2023-07-17 16:53:38 +03:00
b7172df700 Remove most pics from repo 2023-07-17 16:52:23 +03:00
768bd40ad8 Remove most pics from repo 2023-07-17 16:50:27 +03:00
ea27c63f13 Insights from user's answers 2023-07-17 15:59:57 +03:00
c866288b0a Merge remote-tracking branch 'origin/main' into tr/agent_logic 2023-07-17 15:59:37 +03:00
8ae3c60670 In case of new file creation by the MR there is a 404 error on file retrieval by gitlab provider.
It was handled by catching the error and replacing the file string with an empty string.
Type checking was added before byte decoding - necessary in case of the empty string.
2023-07-17 14:53:23 +02:00
f8f415eb75 stable 2023-07-17 15:49:29 +03:00
24583b05f7 Publish GitHub review comments with single API call 2023-07-17 10:41:02 +03:00
fa421fd169 Merge pull request #75 from Codium-ai/bugfix/rename_get_description
get_description was removed
2023-07-17 10:32:01 +03:00
e0ae5c945e get_description was removed 2023-07-17 10:30:44 +03:00
865888e4e8 Merge pull request #74 from Codium-ai/update-gifs
Update GIFs
2023-07-17 09:35:06 +03:00
3b7cfe7bc5 Merge pull request #73 from Codium-ai/hl/clean_comments
Clean comments
2023-07-17 09:33:49 +03:00
262f9dddbc Merge pull request #72 from Codium-ai/tr/minor_fixes
Minor fixes
2023-07-17 09:33:18 +03:00
fa706b6e96 update gifs 2023-07-17 09:30:45 +03:00
ff51ab0946 Add files via upload 2023-07-17 09:27:41 +03:00
7884aa2348 Clean 2023-07-17 09:25:38 +03:00
8f3520807c minor fixes
minor fixes
2023-07-17 08:42:18 +03:00
fa90b242e3 pr_information_from_user_prompts 2023-07-17 08:09:56 +03:00
2dfd34bd61 Merge pull request #71 from Codium-ai/Minor-spelling-fix
Minor Spelling Fix
2023-07-17 08:08:45 +03:00
48f569bef0 Update README.md 2023-07-17 02:39:58 +03:00
a20fb9cc0c Merge pull request #70 from Codium-ai/hl/gitlab_code_suggestion
GitLab Code Suggestions Integration
2023-07-17 02:11:30 +03:00
c58e1f90e7 Merge branch 'main' into hl/gitlab_code_suggestion 2023-07-17 02:10:15 +03:00
d363f148f0 Merge pull request #65 from Codium-ai/tr/agent_logic
pr_information_from_user_prompts
2023-07-17 02:08:56 +03:00
cbf96a2e67 Merge pull request #68 from ilchemla/feature/remove-duplicate-function
Removal of Duplicate Function Across Git Providers
2023-07-17 02:07:09 +03:00
4d87c3ec6a Merge pull request #69 from Codium-ai/readme-update-demo
Update README with new demo instructions and gif
2023-07-17 02:04:50 +03:00
c13c52d733 Merge pull request #66 from Codium-ai/hl/refactor_install_md
Refactor Installation Instructions
2023-07-17 02:02:42 +03:00
dbf8142fe0 Merge with README changes 2023-07-17 02:01:49 +03:00
bacf6c96c2 Merge remote-tracking branch 'origin/hl/refactor_install_md' into hl/refactor_install_md
# Conflicts:
#	README.md
2023-07-17 01:59:46 +03:00
c9d49da8f7 Merge with README changes 2023-07-17 01:54:30 +03:00
7b22edac60 Merge branch 'main' into hl/refactor_install_md
# Conflicts:
#	README.md
2023-07-17 01:50:29 +03:00
fc309f69b9 Support Code Suggestion in Gitlab 2023-07-17 01:44:40 +03:00
7efb5cf74e add demo gif 2023-07-16 22:06:54 +03:00
8e200197c5 update demo description 2023-07-16 22:05:09 +03:00
fe98f67e08 Merge commit '0b1edd9716160bf57eecf307db72439b5443704d' into hl/refactor_install_md
# Conflicts:
#	README.md
2023-07-16 22:02:34 +03:00
0b1edd9716 A small typo 2023-07-16 21:47:50 +03:00
e638dc075c Remove git_provider.get_description() which is a duplicate of git_provider.get_pr_description() 2023-07-16 21:47:48 +03:00
559b160886 Merge pull request #67 from Codium-ai/update-readme-gifs
Update README with new GIFs and minor text corrections
2023-07-16 21:29:23 +03:00
571b8769ac add improve gif 2023-07-16 21:20:11 +03:00
e4bd2148ce update readme 2023-07-16 21:19:40 +03:00
1637bd8774 add improve gif 2023-07-16 21:13:26 +03:00
ce33582d3d Change styling 2023-07-16 21:12:36 +03:00
bc6b592fd9 small fix 2023-07-16 19:58:34 +03:00
24ae6b966f Refactor Install section to a separate MD file 2023-07-16 19:56:58 +03:00
f4de3d2899 pr_information_from_user_prompts 2023-07-16 19:36:20 +03:00
4cacb07ec2 add review gif 2023-07-16 19:27:53 +03:00
2371a9b041 Change titles style 2023-07-16 19:25:10 +03:00
5b7403ae80 Fix typos 2023-07-16 19:15:02 +03:00
e979b8643d Merge pull request #63 from Codium-ai/enhancement/phrasing_update
Enhancement: Update Phrasing and Documentation
2023-07-16 18:18:22 +03:00
05b4f167a3 Fix requirements.txt 2023-07-16 18:05:30 +03:00
2c4245e023 Add github action to README.md 2023-07-16 17:56:32 +03:00
d54ee252ee Improve help text 2023-07-16 17:41:23 +03:00
85eec0b98c Merge pull request #62 from Codium-ai/readme-update-gifs
Readme update gifs
2023-07-16 17:35:02 +03:00
41a988d99a Update readme with gifs 2023-07-16 17:32:48 +03:00
448da3d481 added gifs for ask and describe 2023-07-16 17:23:26 +03:00
b030299547 Merge pull request #51 from ilchemla/feature/new-bitbucket-handler
Adding Bitbucket Provider Support
2023-07-16 17:04:06 +03:00
5bdbfda1e2 Merge pull request #61 from zmeir/zmeir-minor_typos_in_prompt
Minor Typos Fix in Prompt Files
2023-07-16 16:59:49 +03:00
047cfb21f3 Merge pull request #31 from Codium-ai/case-update
update the pr-agent name to title case
2023-07-16 16:50:26 +03:00
35a2497a38 Merge branch 'main' into case-update 2023-07-16 16:49:47 +03:00
99630f83c2 Update README.md 2023-07-16 16:38:38 +03:00
1757f2707c Update README.md 2023-07-16 16:38:32 +03:00
66c44d715c Update README.md 2023-07-16 16:38:25 +03:00
8f7855013a Update README.md 2023-07-16 16:38:15 +03:00
e200be4e57 Update README.md 2023-07-16 16:38:05 +03:00
d0b734bc91 Update README.md 2023-07-16 16:37:57 +03:00
399d5c5c5d Fixed minor typos in promts 2023-07-16 16:37:19 +03:00
1b88049cb0 Update README.md 2023-07-16 16:37:15 +03:00
0304bf05c1 Update README.md 2023-07-16 16:36:11 +03:00
94173cbb06 Merge pull request #58 from Codium-ai/tr/readme_update2
update readme
2023-07-16 16:12:26 +03:00
75447280e4 updated main pic 2023-07-16 16:11:55 +03:00
5edff8b7e4 update readme
update readme
2023-07-16 16:04:06 +03:00
487351d343 Merge pull request #60 from Codium-ai/enhancement/github_app
Enhancement of PR Agent with new commands
2023-07-16 15:59:01 +03:00
93311a9d9b Update GitHub app 2023-07-16 15:42:50 +03:00
704030230f Merge pull request #59 from Codium-ai/enhancement/github_action_apply
Update Github polling
2023-07-16 15:03:44 +03:00
60bce8f049 Update Github polling 2023-07-16 15:00:13 +03:00
e394cb7ddb Merge pull request #54 from Codium-ai/tr/readme_update
Update README.md with Detailed Tool Description and Images
2023-07-16 14:46:29 +03:00
a0e4fb01af Merge remote-tracking branch 'origin/main' into tr/readme_update 2023-07-16 14:42:42 +03:00
eb9190efa1 Merge pull request #56 from Codium-ai/hl/feature_comparison_readme
Add feature comparison overview to Readme file
2023-07-16 14:42:18 +03:00
8cc37d6f59 readme update
readme update
2023-07-16 14:37:59 +03:00
6cc9fe3d06 Merge pull request #57 from Codium-ai/enhancement/github_action_apply
Enhancement: Apply Github Action
2023-07-16 14:30:56 +03:00
0acf423450 Update comment 2023-07-16 14:30:34 +03:00
7958786b4c Github action to work with an image stored on Dockerhub for faster execution 2023-07-16 14:19:02 +03:00
719f3a9dd8 Update README.md 2023-07-16 14:18:48 +03:00
71efd84113 Merge pull request #52 from Codium-ai/enhancement/cli_update
cli.py - modify command line for a more coherent command invokation
2023-07-16 13:51:24 +03:00
25e46a99fd Merge pull request #55 from Codium-ai/enhancement/github_action_apply
Github action support for new style commands
2023-07-16 13:50:57 +03:00
2531849b73 Update usage instructions in PR comment 2023-07-16 13:45:20 +03:00
19f11f99ce Github action support for new style commands 2023-07-16 13:36:02 +03:00
87f978e816 Merge pull request #53 from Codium-ai/enhancement/github_action_apply
Run github action on this repo
2023-07-16 13:23:48 +03:00
7488eb8c9e Run github action on this repo 2023-07-16 13:21:26 +03:00
b3e79ed677 cli.py - modify command line for a more coherent command invokation 2023-07-16 13:18:29 +03:00
5d2fe07bf7 Merge pull request #47 from Codium-ai/feature/github_action
Github custom action development
2023-07-16 12:54:40 +03:00
84bf95e9ab Merge pull request #50 from Codium-ai/tr/numbered_hunks
Adding numbered hunks and code suggestions feature
2023-07-16 12:27:29 +03:00
4f4989af8c full code suggestions
full code suggestions
2023-07-16 09:01:57 +03:00
0a4a604c28 Adding handler for Bitbucket Cloud users 2023-07-15 21:29:36 +03:00
23a249ccdb Merge pull request #48 from Codium-ai/hl/gitlab_fix
Inline suggestion refactor + supporting GitLab
2023-07-14 22:53:52 +03:00
4a6bf4c55a Merge branch 'main' into hl/gitlab_fix 2023-07-14 22:48:13 +03:00
3f75b14ba3 small addition 2023-07-14 22:45:07 +03:00
ae9cedd50d Merge pull request #46 from Codium-ai/tr/description_tool
Add PR Description Tool
2023-07-13 21:00:50 +03:00
ae63833043 Merge commit '055a8ea8590fbe9078cdc6af6398df2f053b9ce7' into hl/gitlab_fix 2023-07-13 20:44:26 +03:00
da6828ad87 Inline suggestion refactor + Gitlab WORKS 2023-07-13 20:43:49 +03:00
ea1cd7ae45 Github custom action development - WIP 2023-07-13 19:14:44 +03:00
1c1aad2806 Github custom action development - WIP 2023-07-13 19:08:10 +03:00
f466d79031 Github custom action development - WIP 2023-07-13 18:59:54 +03:00
e2323dfb9f Github custom action development - WIP 2023-07-13 18:54:40 +03:00
e51e443adc Github custom action development - WIP 2023-07-13 18:54:11 +03:00
f6d4a214ca Github custom action development - WIP 2023-07-13 18:40:03 +03:00
4bb46d9faa Github custom action development - WIP 2023-07-13 18:37:32 +03:00
f337d76af6 Github custom action development - WIP 2023-07-13 18:32:28 +03:00
4e59693c76 diff_files 2023-07-13 18:26:35 +03:00
4033303c1f Github custom action development - WIP 2023-07-13 18:18:23 +03:00
38c8d187d2 Github custom action development - WIP 2023-07-13 18:16:25 +03:00
f8ddfd2f25 Merge remote-tracking branch 'origin/tr/description_tool' into feature/github_action 2023-07-13 18:06:35 +03:00
4b4fda37a6 publish_description as abstract method 2023-07-13 18:04:28 +03:00
9ca6b789a7 Github custom action development - WIP 2023-07-13 18:02:38 +03:00
0f73f5f906 set as title 2023-07-13 17:53:17 +03:00
055a8ea859 Merge pull request #44 from zmeir/patch-1
Typo when setting `openai.api_version`
2023-07-13 17:52:33 +03:00
5742a9be1e Github custom action development 2023-07-13 17:46:12 +03:00
914cc6639a ignore current title 2023-07-13 17:34:18 +03:00
f34cda126a stable 2023-07-13 17:31:28 +03:00
dece20c984 PRDescription 2023-07-13 17:24:56 +03:00
94c1f430af General PR suggestions prompt 2023-07-13 16:34:56 +03:00
9fadde388b remove title and description 2023-07-13 16:26:33 +03:00
d1b6b3bc95 Merge pull request #43 from Codium-ai/tr/inline_code_suggestions
Tr/inline code suggestions
2023-07-13 10:48:42 +03:00
f57d58ee7d Typo when setting openai.api_version 2023-07-13 10:22:57 +03:00
77a451ada0 inline_code_comments 2023-07-13 09:44:33 +03:00
4b8420aa16 remove suggestion number 2023-07-13 08:10:36 +03:00
25bc69f70e Merge pull request #41 from Codium-ai/gitlab_small_fix
Update gitlab config
2023-07-12 18:16:43 +03:00
e2faf117c5 Update gitlab config 2023-07-12 18:02:28 +03:00
aaff03bb60 Merge pull request #40 from Codium-ai/feature/support_azure_openai
Add Azure OpenAI support
2023-07-12 13:37:00 +03:00
973cb2de1c update the pr-agent name to title case 2023-07-10 21:08:14 +03:00
137 changed files with 14817 additions and 1221 deletions

View File

@ -1,2 +1,5 @@
venv/
pr_agent/settings/.secrets.toml
pr_agent/settings/.secrets.toml
pics/
pr_agent.egg-info/
build/

38
.github/workflows/build-and-test.yaml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Build-and-test
on:
push:
pull_request:
types: [ opened, reopened ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- id: checkout
uses: actions/checkout@v2
- id: dockerx
name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- id: build
name: Build dev docker
uses: docker/build-push-action@v2
with:
context: .
file: ./docker/Dockerfile
push: false
load: true
tags: codiumai/pr-agent:test
cache-from: type=gha,scope=dev
cache-to: type=gha,mode=max,scope=dev
target: test
- id: test
name: Test dev docker
run: |
docker run --rm codiumai/pr-agent:test pytest -v

35
.github/workflows/pr-agent-review.yaml vendored Normal file
View File

@ -0,0 +1,35 @@
# This workflow enables developers to call PR-Agents `/[actions]` in PR's comments and upon PR creation.
# Learn more at https://www.codium.ai/pr-agent/
# This is v0.2 of this workflow file
name: PR-Agent
on:
# pull_request:
# issue_comment:
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
pr_agent_job:
runs-on: ubuntu-latest
name: Run pr agent on every pull request
steps:
- name: PR Agent action step
id: pragent
uses: Codium-ai/pr-agent@main
env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
OPENAI_ORG: ${{ secrets.OPENAI_ORG }} # optional
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PINECONE.API_KEY: ${{ secrets.PINECONE_API_KEY }}
PINECONE.ENVIRONMENT: ${{ secrets.PINECONE_ENVIRONMENT }}
GITHUB_ACTION_CONFIG.AUTO_DESCRIBE: true
GITHUB_ACTION_CONFIG.AUTO_REVIEW: true
GITHUB_ACTION_CONFIG.AUTO_IMPROVE: true

7
.gitignore vendored
View File

@ -1,4 +1,9 @@
.idea/
venv/
pr_agent/settings/.secrets.toml
__pycache__
__pycache__
dist/
*.egg-info/
build/
review.md
.DS_Store

7
.pr_agent.toml Normal file
View File

@ -0,0 +1,7 @@
[pr_reviewer]
enable_review_labels_effort = true
enable_auto_approval = true
[pr_code_suggestions]
summarize=true

45
CHANGELOG.md Normal file
View File

@ -0,0 +1,45 @@
## 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.
## 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.
## 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.
- Added support for repository-specific configuration files (.pr_agent.yaml) for the PR Agent.
- 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.
## 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.
## 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.

View File

11
Dockerfile.github_action Normal file
View File

@ -0,0 +1,11 @@
FROM python:3.10 as base
WORKDIR /app
ADD pyproject.toml .
ADD requirements.txt .
RUN pip install . && rm pyproject.toml requirements.txt
ENV PYTHONPATH=/app
ADD pr_agent pr_agent
ADD github_action/entrypoint.sh /
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -0,0 +1 @@
FROM codiumai/pr-agent:github_action

472
INSTALL.md Normal file
View File

@ -0,0 +1,472 @@
## Installation
To get started with PR-Agent quickly, you first need to acquire two tokens:
1. An OpenAI key from [here](https://platform.openai.com/api-keys), with access to GPT-4.
2. A GitHub\GitLab\BitBucket personal access token (classic), with the repo scope. [GitHub from [here](https://github.com/settings/tokens)]
There are several ways to use PR-Agent:
**Locally**
- [Using Docker image (no installation required)](INSTALL.md#use-docker-image-no-installation-required)
- [Run from source](INSTALL.md#run-from-source)
**GitHub specific methods**
- [Run as a GitHub Action](INSTALL.md#run-as-a-github-action)
- [Run as a GitHub App](INSTALL.md#run-as-a-github-app)
- [Deploy as a Lambda Function](INSTALL.md#deploy-as-a-lambda-function)
- [AWS CodeCommit](INSTALL.md#aws-codecommit-setup)
**GitLab specific methods**
- [Run a GitLab webhook server](INSTALL.md#run-a-gitlab-webhook-server)
**BitBucket specific methods**
- [Run as a Bitbucket Pipeline](INSTALL.md#run-as-a-bitbucket-pipeline)
- [Run on a hosted app](INSTALL.md#run-on-a-hosted-bitbucket-app)
- [Bitbucket server and data center](INSTALL.md#bitbucket-server-and-data-center)
---
### Use Docker image (no installation required)
A list of the relevant tools can be found in the [tools guide](./docs/TOOLS_GUIDE.md).
To invoke a tool (for example `review`), you can run directly from the Docker image. Here's how:
- For GitHub:
```
docker run --rm -it -e OPENAI.KEY=<your key> -e GITHUB.USER_TOKEN=<your token> codiumai/pr-agent:latest --pr_url <pr_url> review
```
- For GitLab:
```
docker run --rm -it -e OPENAI.KEY=<your key> -e CONFIG.GIT_PROVIDER=gitlab -e GITLAB.PERSONAL_ACCESS_TOKEN=<your token> codiumai/pr-agent:latest --pr_url <pr_url> review
```
Note: If you have a dedicated GitLab instance, you need to specify the custom url as variable:
```
docker run --rm -it -e OPENAI.KEY=<your key> -e CONFIG.GIT_PROVIDER=gitlab -e GITLAB.PERSONAL_ACCESS_TOKEN=<your token> -e GITLAB.URL=<your gitlab instance url> codiumai/pr-agent:latest --pr_url <pr_url> review
```
- For BitBucket:
```
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=<pr_url> review
```
For other git providers, update CONFIG.GIT_PROVIDER accordingly, and check the `pr_agent/settings/.secrets_template.toml` file for the environment variables expected names and values.
---
If you want to ensure you're running a specific version of the Docker image, consider using the image's digest:
```bash
docker run --rm -it -e OPENAI.KEY=<your key> -e GITHUB.USER_TOKEN=<your token> codiumai/pr-agent@sha256:71b5ee15df59c745d352d84752d01561ba64b6d51327f97d46152f0c58a5f678 --pr_url <pr_url> review
```
Or you can run a [specific released versions](./RELEASE_NOTES.md) of pr-agent, for example:
```
codiumai/pr-agent@v0.9
```
---
### Run from source
1. Clone this repository:
```
git clone https://github.com/Codium-ai/pr-agent.git
```
2. Navigate to the `/pr-agent` folder and install the requirements in your favorite virtual environment:
```
pip install -e .
```
*Note: If you get an error related to Rust in the dependency installation then make sure Rust is installed and in your `PATH`, instructions: https://rustup.rs*
3. Copy the secrets template file and fill in your OpenAI key and your GitHub user token:
```
cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml
chmod 600 pr_agent/settings/.secrets.toml
# Edit .secrets.toml file
```
4. Run the cli.py script:
```
python3 -m pr_agent.cli --pr_url <pr_url> review
python3 -m pr_agent.cli --pr_url <pr_url> ask <your question>
python3 -m pr_agent.cli --pr_url <pr_url> describe
python3 -m pr_agent.cli --pr_url <pr_url> improve
python3 -m pr_agent.cli --pr_url <pr_url> add_docs
python3 -m pr_agent.cli --pr_url <pr_url> generate_labels
python3 -m pr_agent.cli --issue_url <issue_url> similar_issue
...
```
[Optional] Add the pr_agent folder to your PYTHONPATH
```
export PYTHONPATH=$PYTHONPATH:<PATH to pr_agent folder>
```
---
### Run as a GitHub Action
You can use our pre-built Github Action Docker image to run PR-Agent as a Github Action.
1. Add the following file to your repository under `.github/workflows/pr_agent.yml`:
```yaml
on:
pull_request:
types:
- opened
- reopened
- ready_for_review
- review_requested
issue_comment:
types:
- created
- edited
jobs:
pr_agent_job:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
contents: write
name: Run pr agent on every pull request, respond to user comments
steps:
- name: PR Agent action step
id: pragent
uses: Codium-ai/pr-agent@main
env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
** if you want to pin your action to a specific release (v0.7 for example) for stability reasons, use:
```yaml
on:
pull_request:
types:
- opened
- reopened
- ready_for_review
- review_requested
issue_comment:
types:
- created
- edited
jobs:
pr_agent_job:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
contents: write
name: Run pr agent on every pull request, respond to user comments
steps:
- name: PR Agent action step
id: pragent
uses: Codium-ai/pr-agent@v0.7
env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
2. Add the following secret to your repository under `Settings > Secrets and variables > Actions > New repository secret > Add secret`:
```
Name = OPENAI_KEY
Secret = <your key>
```
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](pr_agent/settings/configuration.toml) file. Some examples:
```yaml
env:
# ... previous environment values
OPENAI.ORG: "<Your organization name under your OpenAI account>"
PR_REVIEWER.REQUIRE_TESTS_REVIEW: "false" # Disable tests review
PR_CODE_SUGGESTIONS.NUM_CODE_SUGGESTIONS: 6 # Increase number of code suggestions
```
---
### 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).
- Set the following permissions:
- Pull requests: Read & write
- Issue comment: Read & write
- Metadata: Read-only
- Contents: Read-only
- Set the following events:
- Issue comment
- Pull request
- Push (if you need to enable triggering on PR update)
2. Generate a random secret for your app, and save it for later. For example, you can use:
```
WEBHOOK_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))")
```
3. Acquire the following pieces of information from your app's settings page:
- App private key (click "Generate a private key" and save the file)
- App ID
4. Clone this repository:
```
git clone https://github.com/Codium-ai/pr-agent.git
```
5. Copy the secrets template file and fill in the following:
```
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](./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
secret:
secretName: pr-agent-settings
// ...
containers:
// ...
volumeMounts:
- mountPath: /app/pr_agent/settings_prod
name: settings-volume
```
> Another option is to set the secrets as environment variables in your deployment environment, for example `OPENAI.KEY` and `GITHUB.USER_TOKEN`.
6. Build a Docker image for the app and optionally push it to a Docker repository. We'll use Dockerhub as an example:
```
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
```
7. Host the app using a server, serverless function, or container environment. Alternatively, for development and
debugging, you may use tools like smee.io to forward webhooks to your local machine.
You can check [Deploy as a Lambda Function](#deploy-as-a-lambda-function)
8. Go back to your app's settings, and set the following:
- Webhook URL: The URL of your app's server or the URL of the smee.io channel.
- Webhook secret: The secret you generated earlier.
9. Install the app by navigating to the "Install App" tab and selecting your desired repositories.
> **Note:** When running PR-Agent from GitHub App, the default configuration file (configuration.toml) will be loaded.<br>
> However, you can override the default tool parameters by uploading a local configuration file `.pr_agent.toml`<br>
> For more information please check out the [USAGE GUIDE](./Usage.md#working-with-github-app)
---
### Deploy as a Lambda Function
1. Follow steps 1-5 of [Method 5](#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 <AWS_ACCOUNT>.dkr.ecr.<AWS_REGION>.amazonaws.com/codiumai/pr-agent:serverless
docker push <AWS_ACCOUNT>.dkr.ecr.<AWS_REGION>.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))
7. Go back to steps 8-9 of [Method 5](#run-as-a-github-app) with the function url as your Webhook URL.
The Webhook URL would look like `https://<LAMBDA_FUNCTION_URL>/api/v1/github_webhooks`
---
### AWS CodeCommit Setup
Not all features have been added to CodeCommit yet. As of right now, CodeCommit has been implemented to run the pr-agent 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 pr-agent 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
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]`
##### 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
```
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"codecommit:BatchDescribe*",
"codecommit:BatchGet*",
"codecommit:Describe*",
"codecommit:EvaluatePullRequestApprovalRules",
"codecommit:Get*",
"codecommit:List*",
"codecommit:PostComment*",
"codecommit:PutCommentReaction",
"codecommit:UpdatePullRequestDescription",
"codecommit:UpdatePullRequestTitle"
],
"Resource": "*"
}
]
}
```
##### AWS CodeCommit Access Key and Secret
Example setting the Access Key and Secret using environment variables
```sh
export AWS_ACCESS_KEY_ID="XXXXXXXXXXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXXXXXXXXX"
export AWS_DEFAULT_REGION="us-east-1"
```
##### AWS CodeCommit CLI Example
After you set up AWS CodeCommit using the instructions above, here is an example CLI run that tells pr-agent to **review** a given pull request.
(Replace your specific PYTHONPATH and PR URL in the example)
```sh
PYTHONPATH="/PATH/TO/PROJECTS/pr-agent" python pr_agent/cli.py \
--pr_url https://us-east-1.console.aws.amazon.com/codesuite/codecommit/repositories/MY_REPO_NAME/pull-requests/321 \
review
```
---
### Run a GitLab webhook server
1. From the GitLab workspace or group, create an access token. Enable the "api" scope only.
2. Generate a random secret for your app, and save it for later. For example, you can use:
```
WEBHOOK_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))")
```
3. Follow the instructions to build the Docker image, setup a secrets file and deploy on your own server from [Method 5](#run-as-a-github-app) steps 4-7. Be sure to set the target to `gitlab_webhook` instead of `github_app` when building the Docker image.
4. In the secrets file, fill in the following:
- Your OpenAI key.
- In the [gitlab] section, fill in personal_access_token and shared_secret. The access token can be a personal access token, or a group or project access token.
- Set deployment_type to 'gitlab' in [configuration.toml](./pr_agent/settings/configuration.toml)
5. Create a webhook in GitLab. Set the URL to the URL of your app's server with the path `/webhook` (e.g. `http://pr-agent.example.com:3000/webhook`). Set the secret token to the generated secret from step 2.
In the "Trigger" section, check the comments and merge request events boxes.
6. Test your installation by opening a merge request or commenting on a merge request using one of CodiumAI's commands.
### 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.10
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: <your key>
BITBUCKET_BEARER_TOKEN: <your token>
You can get a Bitbucket token for your repository by following Repository Settings -> Security -> Access Tokens.
Note that comments on a PR are not supported in Bitbucket Pipeline.
### Run using CodiumAI-hosted Bitbucket app
Please contact <support@codium.ai> or visit [CodiumAI pricing page](https://www.codium.ai/pricing/) if you're interested in a hosted BitBucket app solution that provides full functionality including PR reviews and comment handling. It's based on the [bitbucket_app.py](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/git_providers/bitbucket_provider.py) implementation.
### Bitbucket Server and Data Center
Login into your on-prem instance of Bitbucket with your service account username and password.
Navigate to `Manage account`, `HTTP Access tokens`, `Create Token`.
Generate the token and add it to .secret.toml under `bitbucket_server` section
```toml
[bitbucket_server]
bearer_token = "<your key>"
```
#### Run it as CLI
Modify `configuration.toml`:
```toml
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
```
#### 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
```
Navigate to `Projects` or `Repositories`, `Settings`, `Webhooks`, `Create Webhook`.
Fill the name and URL, Authentication None select the Pull Request Opened checkbox to receive that event as webhook.
The URL should end with `/webhook`, for example: https://domain.com/webhook
=======

View File

@ -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,13 +25,13 @@ 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
#### 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. Withing each language we sort the files by the number of tokens in the file (in descending order):
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]]```
2. Iterate through the patches in the order described above
2. Add the patches to the prompt until the prompt reaches a certain buffer from the max token length
@ -39,4 +39,4 @@ We use [tiktoken](https://github.com/openai/tiktoken) to tokenize the patches af
4. If we haven't reached the max token length, add the `deleted files` to the prompt until the prompt reaches the max token length (hard stop), skip the rest of the patches.
### Example
![](./pics/git_patch_logic.png)
<kbd><img src=https://codium.ai/images/git_patch_logic.png width="768"></kbd>

542
README.md
View File

@ -2,318 +2,300 @@
<div align="center">
<img src="./pics/logo-dark.png#gh-dark-mode-only" width="250"/>
<img src="./pics/logo-light.png#gh-light-mode-only" width="250"/>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://codium.ai/images/pr_agent/logo-dark.png" width="330">
<source media="(prefers-color-scheme: light)" srcset="https://codium.ai/images/pr_agent/logo-light.png" width="330">
<img alt="logo">
</picture>
<br/>
Making pull requests less painful with an AI agent
</div>
[![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)
CodiumAI `pr-agent` is an open-source tool aiming to help developers review PRs faster and more efficiently. It automatically analyzes the PR, provides feedback and suggestions, and can answer free-text questions.
[![Twitter](https://img.shields.io/twitter/follow/codiumai)](https://twitter.com/codiumai)
<a href="https://github.com/Codium-ai/pr-agent/commits/main">
<img alt="GitHub" src="https://img.shields.io/github/last-commit/Codium-ai/pr-agent/main?style=for-the-badge" height="20">
</a>
</div>
- [Live demo](#live-demo)
- [Quickstart](#Quickstart)
- [Usage and tools](#usage-and-tools)
- [Configuration](#Configuration)
## Table of Contents
- [News and Updates](#news-and-updates)
- [Overview](#overview)
- [Example results](#example-results)
- [Try it now](#try-it-now)
- [Installation](#installation)
- [PR-Agent Pro 💎](#pr-agent-pro-)
- [How it works](#how-it-works)
- [Roadmap](#roadmap)
- [Similar projects](#similar-projects)
- [Why use PR-Agent?](#why-use-pr-agent)
## News and Updates
## Live demo
### Feb 29, 2024
- You can now use the repo's [wiki page](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#configuration-options) to set configurations for PR-Agent 💎
Experience GPT-4 powered PR review on your public GitHub repository with our hosted pr-agent. To try it, just mention `@CodiumAI-Agent` in any PR comment! The agent will generate a PR review in response.
<kbd><img src="https://codium.ai/images/pr_agent/wiki_configuration.png" width="512"></kbd>
![Review generation process](./pics/pr-agent-review-process1.gif)
To set up your own pr-agent, see the [Quickstart](#Quickstart) section
### Feb 21, 2024
- Added a new command, `/help`, to easily provide a list of available tools and their descriptions, and run them interactively.
<kbd>
<img src="https://www.codium.ai/images/pr_agent/help.png" width="512">
</kbd>
### Feb 18, 2024
- Introducing the `CI Feedback` tool 💎. The tool automatically triggers when a PR has a failed check. It analyzes the failed check, and provides summarized logs and analysis. Note that this feature requires read access to GitHub 'checks' and 'actions'. See [here](./docs/CI_FEEDBACK.md) for more details.
- New ability - you can run `/ask` on specific lines of code in the PR from the PR's diff view. See [here](./docs/ASK.md#ask-lines) for more details.
- Introducing support for [Azure DevOps Webhooks](./Usage.md#azure-devops-webhook), as well as bug fixes and improved support for several ADO commands.
## Overview
<div style="text-align:left;">
CodiumAI PR-Agent is an open-source tool to help efficiently review and handle pull requests.
- See the [Installation Guide](./INSTALL.md) for instructions on installing and running the tool on different git platforms.
- See the [Usage Guide](./Usage.md) for instructions on running the PR-Agent commands via different interfaces, including _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened.
- See the [Tools Guide](./docs/TOOLS_GUIDE.md) for a detailed description of the different tools (tools are run via the commands).
Supported commands per platform:
| | | GitHub | Gitlab | Bitbucket | Azure DevOps |
|-------|-----------------------------------------------------------------------------------------------------------------------------------------|:--------------------:|:--------------------:|:--------------------:|:--------------------:|
| TOOLS | Review | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | ⮑ Incremental | :white_check_mark: | | | |
| | ⮑ [SOC2 Compliance](https://github.com/Codium-ai/pr-agent/blob/main/docs/REVIEW.md#soc2-ticket-compliance-) 💎 | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Describe | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | ⮑ [Inline File Summary](https://github.com/Codium-ai/pr-agent/blob/main/docs/DESCRIBE.md#inline-file-summary-) 💎 | :white_check_mark: | | | |
| | Improve | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | ⮑ Extended | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Ask | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | ⮑ [Ask on code lines](./docs/ASK.md#ask-lines) | :white_check_mark: | :white_check_mark: | | |
| | [Custom Suggestions](https://github.com/Codium-ai/pr-agent/blob/main/docs/CUSTOM_SUGGESTIONS.md) 💎 | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | [Test](https://github.com/Codium-ai/pr-agent/blob/main/docs/TEST.md) 💎 | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
| | Reflect and Review | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Update CHANGELOG.md | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Find Similar Issue | :white_check_mark: | | | |
| | [Add PR Documentation](https://github.com/Codium-ai/pr-agent/blob/main/docs/ADD_DOCUMENTATION.md) 💎 | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
| | [Custom Labels](https://github.com/Codium-ai/pr-agent/blob/main/docs/DESCRIBE.md#handle-custom-labels-from-the-repos-labels-page-gem) 💎 | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
| | [Analyze](https://github.com/Codium-ai/pr-agent/blob/main/docs/Analyze.md) 💎 | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
| | [CI Feedback](https://github.com/Codium-ai/pr-agent/blob/main/docs/CI_FEEDBACK.md) 💎 | :white_check_mark: | | | |
| | | | | | |
| USAGE | CLI | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | App / webhook | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Tagging bot | :white_check_mark: | | | |
| | Actions | :white_check_mark: | | :white_check_mark: | |
| | | | | | |
| CORE | PR compression | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Repo language prioritization | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Adaptive and token-aware file patch fitting | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Multiple models support | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | [Static code analysis](https://github.com/Codium-ai/pr-agent/blob/main/docs/Analyze.md) 💎 | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | [Global and wiki configurations](./Usage.md#configuration) 💎 | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | [PR Actions](https://www.codium.ai/images/pr_agent/pr-actions.mp4) 💎 | :white_check_mark: | | | |
- 💎 means this feature is available only in [PR-Agent Pro](https://www.codium.ai/pricing/)
- Support for additional git providers is described in [here](./docs/Full_environments.md)
___
**Auto Description ([`/describe`](./docs/DESCRIBE.md))**: Automatically generating PR description - title, type, summary, code walkthrough and labels.
\
**Auto Review ([`/review`](./docs/REVIEW.md))**: Adjustable feedback about the PR, possible issues, security concerns, review effort and more.
\
**Question Answering ([`/ask ...`](./docs/ASK.md))**: Answering free-text questions about the PR.
\
**Code Suggestions ([`/improve`](./docs/IMPROVE.md))**: Code suggestions for improving the PR.
\
**Update Changelog ([`/update_changelog`](./docs/UPDATE_CHANGELOG.md))**: Automatically updating the CHANGELOG.md file with the PR changes.
\
**Find Similar Issue ([`/similar_issue`](./docs/SIMILAR_ISSUE.md))**: Automatically retrieves and presents similar issues.
\
**Add Documentation 💎 ([`/add_docs`](./docs/ADD_DOCUMENTATION.md))**: Generates documentation to methods/functions/classes that changed in the PR.
\
**Generate Custom Labels 💎 ([`/generate_labels`](./docs/GENERATE_CUSTOM_LABELS.md))**: Generates custom labels for the PR, based on specific guidelines defined by the user.
\
**Analyze 💎 ([`/analyze`](./docs/Analyze.md))**: Identify code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component.
\
**Custom Suggestions 💎 ([`/custom_suggestions`](./docs/CUSTOM_SUGGESTIONS.md))**: Automatically generates custom suggestions for improving the PR code, based on specific guidelines defined by the user.
\
**Generate Tests 💎 ([`/test component_name`](./docs/TEST.md))**: Automatically generates unit tests for a selected component, based on the PR code changes.
\
**CI Feedback 💎 ([`/checks ci_job`](./docs/CI_FEEDBACK.md))**: Automatically generates feedback and analysis for a failed CI job.
___
## Example results
</div>
<h4><a href="https://github.com/Codium-ai/pr-agent/pull/530">/describe</a></h4>
<div align="center">
<p float="center">
<img src="https://www.codium.ai/images/pr_agent/describe_new_short_main.png" width="512">
</p>
</div>
<hr>
<h4><a href="https://github.com/Codium-ai/pr-agent/pull/732#issuecomment-1975099151">/review</a></h4>
<div align="center">
<p float="center">
<kbd>
<img src="https://www.codium.ai/images/pr_agent/review_new_short_main.png" width="512">
</kbd>
</p>
</div>
<hr>
<h4><a href="https://github.com/Codium-ai/pr-agent/pull/732#issuecomment-1975099159">/improve</a></h4>
<div align="center">
<p float="center">
<kbd>
<img src="https://www.codium.ai/images/pr_agent/improve_new_short_main.png" width="512">
</kbd>
</p>
</div>
<hr>
<h4><a href="https://github.com/Codium-ai/pr-agent/pull/530">/generate_labels</a></h4>
<div align="center">
<p float="center">
<kbd><img src="https://www.codium.ai/images/pr_agent/geneare_custom_labels_main_short.png" width="300"></kbd>
</p>
</div>
[//]: # (<h4><a href="https://github.com/Codium-ai/pr-agent/pull/78#issuecomment-1639739496">/reflect_and_review:</a></h4>)
[//]: # (<div align="center">)
[//]: # (<p float="center">)
[//]: # (<img src="https://www.codium.ai/images/reflect_and_review.gif" width="800">)
[//]: # (</p>)
[//]: # (</div>)
[//]: # (<h4><a href="https://github.com/Codium-ai/pr-agent/pull/229#issuecomment-1695020538">/ask:</a></h4>)
[//]: # (<div align="center">)
[//]: # (<p float="center">)
[//]: # (<img src="https://www.codium.ai/images/ask-2.gif" width="800">)
[//]: # (</p>)
[//]: # (</div>)
[//]: # (<h4><a href="https://github.com/Codium-ai/pr-agent/pull/229#issuecomment-1695024952">/improve:</a></h4>)
[//]: # (<div align="center">)
[//]: # (<p float="center">)
[//]: # (<img src="https://www.codium.ai/images/improve-2.gif" width="800">)
[//]: # (</p>)
[//]: # (</div>)
<div align="left">
</div>
<hr>
## Try it now
Try the GPT-4 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
![Review generation process](https://www.codium.ai/images/demo-2.gif)
To set up your own PR-Agent, see the [Installation](#installation) section below.
Note that when you set your own PR-Agent or use CodiumAI hosted PR-Agent, there is no need to mention `@CodiumAI-Agent ...`. Instead, directly start with the command, e.g., `/ask ...`.
---
## Quickstart
To get started with pr-agent quickly, you first need to acquire two tokens:
## Installation
To use your own version of PR-Agent, you first need to acquire two tokens:
1. An OpenAI key from [here](https://platform.openai.com/), with access to GPT-4.
2. A GitHub personal access token (classic) with the repo scope.
There are several ways to use pr-agent. Let's start with the simplest one:
There are several ways to use PR-Agent:
---
**Locally**
- [Use Docker image (no installation required)](./INSTALL.md#use-docker-image-no-installation-required)
- [Run from source](./INSTALL.md#run-from-source)
#### Method 1: Use Docker image (no installation required)
**GitHub specific methods**
- [Run as a GitHub Action](./INSTALL.md#run-as-a-github-action)
- [Run as a GitHub App](./INSTALL.md#run-as-a-github-app)
To request a review for a PR, or ask a question about a PR, you can run directly from the Docker image. Here's how:
**GitLab specific methods**
- [Run a GitLab webhook server](./INSTALL.md#run-a-gitlab-webhook-server)
1. To request a review for a PR, run the following command:
**BitBucket specific methods**
- [Run as a Bitbucket Pipeline](./INSTALL.md#run-as-a-bitbucket-pipeline)
```
docker run --rm -it -e OPENAI.KEY=<your key> -e GITHUB.USER_TOKEN=<your token> codiumai/pr-agent --pr_url <pr url>
```
## PR-Agent Pro 💎
[PR-Agent Pro](https://www.codium.ai/pricing/) is a hosted version of PR-Agent, provided by CodiumAI. 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 PR-Agent app to your GitHub\BitBucket repo.
2. **Improved privacy** - No data will be stored or used to train models. PR-Agent Pro will employ zero data retention, and will use an OpenAI account with zero data retention.
3. **Improved support** - PR-Agent Pro 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, PR-Agent Pro will emphasize more customization, and the usage of static code analysis, in addition to LLM logic, to improve results. It has the following additional tools and features:
- [**Analyze PR components**](https://github.com/Codium-ai/pr-agent/blob/main/docs/Analyze.md)
- [**Custom Code Suggestions**](https://github.com/Codium-ai/pr-agent/blob/main/docs/CUSTOM_SUGGESTIONS.md)
- [**Tests**](https://github.com/Codium-ai/pr-agent/blob/main/docs/TEST.md)
- [**PR documentation**](https://github.com/Codium-ai/pr-agent/blob/main/docs/ADD_DOCUMENTATION.md)
- [**SOC2 compliance check**](https://github.com/Codium-ai/pr-agent/blob/main/docs/REVIEW.md#soc2-ticket-compliance-)
- [**Custom labels**](https://github.com/Codium-ai/pr-agent/blob/main/docs/DESCRIBE.md#handle-custom-labels-from-the-repos-labels-page-gem)
- [**Global configuration**](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#global-configuration-file-)
2. To ask a question about a PR, run the following command:
```
docker run --rm -it -e OPENAI.KEY=<your key> -e GITHUB.USER_TOKEN=<your token> codiumai/pr-agent --pr_url <pr url> --question "<your question>"
```
Possible questions you can ask include:
- What is the main theme of this PR?
- Is the PR ready for merge?
- What are the main changes in this PR?
- Should this PR be split into smaller parts?
- Can you compose a rhymed song about this PR.
---
#### Method 2: Run from source
1. Clone this repository:
```
git clone https://github.com/Codium-ai/pr-agent.git
```
2. Install the requirements in your favorite virtual environment:
```
pip install -r requirements.txt
```
3. Copy the secrets template file and fill in your OpenAI key and your GitHub user token:
```
cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml
# Edit .secrets.toml file
```
4. Run the appropriate Python scripts from the scripts folder:
```
python pr_agent/cli.py --pr_url <pr url>
python pr_agent/cli.py --pr_url <pr url> --question "<your question>"
```
---
#### Method 3: Method 3: Run as a polling server; request reviews by tagging your Github user on a PR
Follow steps 1-3 of method 2.
Run the following command to start the server:
```
python pr_agent/servers/github_polling.py
```
---
#### Method 4: 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).
- Set the following permissions:
- Pull requests: Read & write
- Issue comment: Read & write
- Metadata: Read-only
- Set the following events:
- Issue comment
- Pull request
2. Generate a random secret for your app, and save it for later. For example, you can use:
```
WEBHOOK_SECRET=$(python -c "import secrets; print(secrets.token_hex(10))")
```
3. Acquire the following pieces of information from your app's settings page:
- App private key (click "Generate a private key", and save the file)
- App ID
4. Clone this repository:
```
git clone https://github.com/Codium-ai/pr-agent.git
```
5. Copy the secrets template file and fill in the following:
- Your OpenAI key.
- Set deployment_type to 'app'
- 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.
```
cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml
# Edit .secrets.toml file
```
6. Build a Docker image for the app and optionally push it to a Docker repository. We'll use Dockerhub as an example:
```
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
```
7. Host the app using a server, serverless function, or container environment. Alternatively, for development and
debugging, you may use tools like smee.io to forward webhooks to your local machine.
8. Go back to your app's settings, set the following:
- Webhook URL: The URL of your app's server, or the URL of the smee.io channel.
- Webhook secret: The secret you generated earlier.
9. Install the app by navigating to the "Install App" tab, and selecting your desired repositories.
---
## Usage and Tools
CodiumAI pr-agent provides two types of interactions ("tools"): `"PR Reviewer"` and `"PR Q&A"`.
- The "PR Reviewer" tool automatically analyzes PRs, and provides different types of feedbacks.
- The "PR Q&A" tool answers free-text questions about the PR.
### PR Reviewer
Here is a quick overview of the different sub-tools of PR Reviewer:
- PR Analysis
- Summarize main theme
- PR description and title
- PR type classification
- Is the PR covered by relevant tests
- Is this a focused PR
- Are there security concerns
- PR Feedback
- General PR suggestions
- Code suggestions
This is how a typical output of the PR Reviewer looks like:
---
#### PR Analysis
- 🎯 **Main theme:** Adding language extension handler and token handler
- 🔍 **Description and title:** Yes
- 📌 **Type of PR:** Enhancement
- 🧪 **Relevant tests added:** No
-**Focused PR:** Yes, the PR is focused on adding two new handlers for language extension and token counting.
- 🔒 **Security concerns:** No, the PR does not introduce possible security concerns or issues.
#### PR Feedback
- 💡 **General PR suggestions:** The PR is generally well-structured and the code is clean. However, it would be beneficial to add some tests to ensure the new handlers work as expected. Also, consider adding docstrings to the new functions and classes to improve code readability and maintainability.
- 🤖 **Code suggestions:**
- **relevant file:** pr_agent/algo/language_handler.py
**suggestion content:** Consider using a set instead of a list for 'bad_extensions' as checking membership in a set is faster than in a list. [medium]
- **relevant file:** pr_agent/algo/language_handler.py
**suggestion content:** In the 'filter_bad_extensions' function, you are splitting the filename on '.' and taking the last element to get the extension. This might not work as expected if the filename contains multiple '.' characters. Consider using 'os.path.splitext' to get the file extension more reliably. [important]
---
### PR Q&A
This tool answers free-text questions about the PR. This is how a typical output of the PR Q&A looks like:
**Question**: summarize for me the PR in 4 bullet points
**Answer**:
- The PR introduces a new feature to sort files by their main languages. It uses a mapping of programming languages to their file extensions to achieve this.
- It also introduces a filter to exclude files with certain extensions, deemed as 'bad extensions', from the sorting process.
- The PR modifies the `get_pr_diff` function in `pr_processing.py` to use the new sorting function. It also refactors the code to move the PR pruning logic into a separate function.
- A new `TokenHandler` class is introduced in `token_handler.py` to handle token counting operations. This class is initialized with a PR, variables, system, and user, and provides methods to get system and user tokens and to count tokens in a patch.
---
## Configuration
The different tools and sub-tools used by CodiumAI pr-agent are easily configurable via the configuration file: `/settings/configuration.toml`.
#### Enabling/disabling sub-tools:
You can enable/disable the different PR Reviewer sub-sections with the following flags:
```
require_focused_review=true
require_tests_review=true
require_security_review=true
```
#### Code Suggestions configuration:
There are also configuration options to control different aspects of the `code suggestions` feature.
The number of suggestions provided can be controlled by adjusting the following parameter:
```
num_code_suggestions=4
```
You can also enable more verbose and informative mode of code suggestions:
```
extended_code_suggestions=false
```
This is a comparison of the regular and extended code suggestions modes:
- **relevant file:** sql.py
- **suggestion content:** Remove hardcoded sensitive information like username and password. Use environment variables or a secure method to store these values. [important]
Example for extended suggestion:
- **relevant file:** sql.py
- **suggestion content:** Remove hardcoded sensitive information (username and password) [important]
- **why:** Hardcoding sensitive information is a security risk. It's better to use environment variables or a secure way to store these values.
- **code example:**
- **before code:**
```
user = "root",
password = "Mysql@123",
```
- **after code:**
```
user = os.getenv('DB_USER'),
password = os.getenv('DB_PASSWORD'),
```
---
## How it works
![PR-Agent Tools](./pics/pr_agent_overview.png)
The following diagram illustrates PR-Agent tools and their flow:
![PR-Agent Tools](https://codium.ai/images/pr_agent/diagram-v0.9.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
## Roadmap
## Why use PR-Agent?
- [ ] Support open-source models, as a replacement for openai models. Note that a minimal requirement for each open-source model is to have 8k+ context, and good support for generating json as an output
- [ ] Support other Git providers, such as Gitlab and Bitbucket.
- [ ] Develop additional logics for handling large PRs, and compressing git patches
- [ ] Dedicated tools and sub-tools for specific programming languages (Python, Javascript, Java, C++, etc)
- [ ] Add additional context to the prompt. For example, repo (or relevant files) summarization, with tools such a [ctags](https://github.com/universal-ctags/ctags)
- [ ] Adding more tools. Possible directions:
- [ ] Code Quality
- [ ] Coding Style
- [ ] Performance (are there any performance issues)
- [ ] Documentation (is the PR properly documented)
- [ ] Rank the PR importance
- [ ] ...
A reasonable question that can be asked is: `"Why use PR-Agent? What makes it stand out from existing tools?"`
## Similar Projects
Here are some advantages of PR-Agent:
- [CodiumAI - Meaningful tests for busy devs](https://github.com/Codium-ai/codiumai-vscode-release)
- [Aider - GPT powered coding in your terminal](https://github.com/paul-gauthier/aider)
- [GPT-Engineer](https://github.com/AntonOsika/gpt-engineer)
- [CodeReview BOT](https://github.com/anc95/ChatGPT-CodeReview)
- [AI-Maintainer](https://github.com/merwanehamadi/AI-Maintainer)
- We emphasize **real-life practical usage**. Each tool (review, improve, ask, ...) has a single GPT-4 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](./PR_COMPRESSION.md) 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-4, GPT-3.5, Anthropic, Cohere, Llama2).
## Data privacy
If you host PR-Agent with your OpenAI API key, it is between you and OpenAI. You can read their API data privacy policy here:
https://openai.com/enterprise-privacy
When using PR-Agent Pro 💎, hosted by CodiumAI, we will not store any of your data, nor will we use it for training.
You will also benefit from an OpenAI account with zero data retention.
## Links
[![Join our Discord community](https://raw.githubusercontent.com/Codium-ai/codiumai-vscode-release/main/media/docs/Joincommunity.png)](https://discord.gg/kG35uSHDBc)
- Discord community: https://discord.gg/kG35uSHDBc
- CodiumAI site: https://codium.ai
- Blog: https://www.codium.ai/blog/
- Troubleshooting: https://www.codium.ai/blog/technical-faq-and-troubleshooting/
- Support: support@codium.ai

103
RELEASE_NOTES.md Normal file
View File

@ -0,0 +1,103 @@
## [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
- codiumai/pr-agent:0.11-gitlab_webhook
- codiumai/pr-agent:0.11-github_polling
- 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
## [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
- codiumai/pr-agent:0.10-gitlab_webhook
- codiumai/pr-agent:0.10-github_polling
- 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)
- Support ['gpt-4-1106-preview'](https://github.com/Codium-ai/pr-agent/pull/437/files) model
- Support for Google's [Vertex AI](https://github.com/Codium-ai/pr-agent/pull/436)
- Implementing [thresholds](https://github.com/Codium-ai/pr-agent/pull/423) for incremental PR reviews
- 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
- codiumai/pr-agent:0.9-gitlab_webhook
- codiumai/pr-agent:0.9-github_polling
- 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)
- GitHub Action: Can now use a `.pr_agent.toml` file to control configuration parameters (see [Usage Guide](./Usage.md#working-with-github-action)).
- GitHub App: Added ability to trigger tools on [push events](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools-for-new-code-pr-push)
- Support custom domain URLs for Azure devops integration (see [link](https://github.com/Codium-ai/pr-agent/pull/381)).
- 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
- 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
- codiumai/pr-agent:0.7
- codiumai/pr-agent:0.7-github_app
- codiumai/pr-agent:0.7-bitbucket-app
- codiumai/pr-agent:0.7-gitlab_webhook
- codiumai/pr-agent:0.7-github_polling
- 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.

529
Usage.md Normal file
View File

@ -0,0 +1,529 @@
## Usage Guide
### Table of Contents
- [Introduction](#introduction)
- [Configuration Options](#configuration-options)
- [Managing Mail Notifications](#managing-mail-notifications)
- [Usage Types](#usage-types)
- [Local Repo (CLI)](#working-from-a-local-repo-cli)
- [Online Usage](#online-usage)
- [GitHub App](#working-with-github-app)
- [GitHub Action](#working-with-github-action)
- [GitLab Webhook](#working-with-gitlab-webhook)
- [BitBucket App](#working-with-bitbucket-self-hosted-app)
- [Azure DevOps Provider](#azure-devops-provider)
- [Additional Configurations Walkthrough](#appendix---additional-configurations-walkthrough)
- [Ignoring files from analysis](#ignoring-files-from-analysis)
- [Extra instructions](#extra-instructions)
- [Working with large PRs](#working-with-large-prs)
- [Changing a model](#changing-a-model)
- [Patch Extra Lines](#patch-extra-lines)
- [Editing the prompts](#editing-the-prompts)
## Introduction
After [installation](/INSTALL.md), 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
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.
### 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", "bitbucket", "azure", "codecommit", "local", "gerrit"
`
## Configuration Options
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.
See the [Tools Guide](./docs/TOOLS_GUIDE.md) for a detailed description of the different tools and their configurations.
There are three ways to set persistent configurations:
1. Wiki configuration page 💎
2. Local configuration file
3. Global configuration file 💎
In terms of precedence, wiki configurations will override local configurations, and local configurations will override global configurations.
### Wiki configuration file 💎
Specifically for GitHub, with PR-Agent-Pro 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**.
<kbd><img src="https://codium.ai/images/pr_agent/wiki_configuration.png" width="512"></kbd>
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, to allow better presentation when displayed in the wiki as markdown.
An example content:
\`\`\`<br>
[pr_description] # /describe #<br>
keep_original_user_title=false<br>
\`\`\`
PR-Agent will know to remove the triple-quotes when reading the configuration content.
### Local configuration file
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.
For example, if you set in `.pr_agent.toml`:
```
[pr_reviewer]
extra_instructions="""\
- instruction a
- instruction b
...
"""
```
Then you can give a list of extra instructions to the `review` tool.
### Global configuration file 💎
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.
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`:
- The repo [`https://github.com/Codium-ai/pr-agent-settings`](https://github.com/Codium-ai/pr-agent-settings/blob/main/.pr_agent.toml) contains a `.pr_agent.toml` file that serves as a global configuration file for all the repos 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`.
## Managing mail notifications
Unfortunately, it is not possible in GitHub to disable mail notifications from a specific user.
If you are subscribed to notifications for a repo with PR-Agent, we recommend turning off notifications for PR comments, to avoid lengthy emails:
<kbd><img src="https://codium.ai/images/pr_agent/notifications.png" width="512"></kbd>
As an alternative, you can filter in your mail provider the notifications specifically from the PR-Agent bot:
https://www.quora.com/How-can-you-filter-emails-for-specific-people-in-Gmail#:~:text=On%20the%20Filters%20and%20Blocked,the%20body%20of%20the%20email
<kbd><img src="https://codium.ai/images/pr_agent/filter_mail_notifications.png" width="512"></kbd>
## Usage Types
### Working from a local repo (CLI)
When running from your local repo (CLI), your local configuration file will be used.
Examples of invoking the different tools via the CLI:
- **Review**: `python -m pr_agent.cli --pr_url=<pr_url> review`
- **Describe**: `python -m pr_agent.cli --pr_url=<pr_url> describe`
- **Improve**: `python -m pr_agent.cli --pr_url=<pr_url> improve`
- **Ask**: `python -m pr_agent.cli --pr_url=<pr_url> ask "Write me a poem about this PR"`
- **Reflect**: `python -m pr_agent.cli --pr_url=<pr_url> reflect`
- **Update Changelog**: `python -m pr_agent.cli --pr_url=<pr_url> update_changelog`
`<pr_url>` is the url of the relevant PR (for example: https://github.com/Codium-ai/pr-agent/pull/50).
**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=<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.
### Online usage
Online usage means invoking PR-Agent tools by [comments](https://github.com/Codium-ai/pr-agent/pull/229#issuecomment-1695021901) on a PR.
Commands for invoking the different tools via comments:
- **Review**: `/review`
- **Describe**: `/describe`
- **Improve**: `/improve`
- **Ask**: `/ask "..."`
- **Reflect**: `/reflect`
- **Update Changelog**: `/update_changelog`
To edit a specific configuration value, just add `--config_path=<value>` 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](pr_agent/settings/configuration.toml) file can be similarly edited. Comment `/config` to see the list of available configurations.
### Working with GitHub App
#### GitHub app automatic tools when a new PR is opened
The [github_app](pr_agent/settings/configuration.toml#L108) 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.
```
[github_app]
pr_commands = [
"/describe --pr_description.add_original_user_description=true --pr_description.keep_original_user_title=true",
"/review --pr_reviewer.num_code_suggestions=0",
"/improve",
]
```
This means that when a new PR is opened/reopened or marked as ready for review, PR-Agent will run the `describe`, `review` and `improve` tools.
For the `describe` tool, for example, the `add_original_user_description` and `keep_original_user_title` parameters will be set to true.
You can override the default tool parameters by using one the three options for a [configuration file](#configuration-options): **wiki**, **local**, or **global**.
For example, if your local `.pr_agent.toml` file contains:
```
[pr_description]
add_original_user_description = false
keep_original_user_title = false
```
When a new PR is opened, PR-Agent will run the `describe` tool with the above parameters.
To cancel the automatic run of all the tools, set:
```
[github_app]
handle_pr_actions = []
```
You can also disable automatic runs for PRs with specific titles, by setting the `ignore_pr_titles` parameter with the relevant regex. For example:
```
[github_app]
ignore_pr_title = ["^[Auto]", ".*ignore.*"]
```
will ignore PRs with titles that start with "Auto" or contain the word "ignore".
#### GitHub app automatic tools for push actions (commits to an open PR)
In addition to running automatic tools when a PR is opened, the GitHub app can also respond to new code that is pushed to an open PR.
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.
```
[github_app]
handle_push_trigger = true
push_commands = [
"/describe --pr_description.add_original_user_description=true --pr_description.keep_original_user_title=true",
"/review --pr_reviewer.num_code_suggestions=0",
]
```
This means that when new code is pushed to the PR, the PR-Agent will run the `describe` and `review` tools, with the specified parameters.
### Working with GitHub Action
`GitHub Action` is a different way to trigger PR-Agent 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
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Make sure to add your GitHub token to your repo secrets
github_action_config.auto_review: "true" # enable\disable auto review
github_action_config.auto_describe: "true" # enable\disable auto describe
github_action_config.auto_improve: "true" # enable\disable auto improve
```
`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.
Note that you can give additional config parameters by adding environment variables to `.github/workflows/pr_agent.yml`, or by using a `.pr_agent.toml` file in the root of your repo, similar to the GitHub App usage.
For example, you can set an environment variable: `pr_description.add_original_user_description=false`, or add a `.pr_agent.toml` file with the following content:
```
[pr_description]
add_original_user_description = false
```
### Working with GitLab Webhook
After setting up a GitLab 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:
```
[gitlab]
pr_commands = [
"/describe --pr_description.add_original_user_description=true --pr_description.keep_original_user_title=true",
"/review --pr_reviewer.num_code_suggestions=0",
"/improve",
]
```
### Working with BitBucket Self-Hosted App
Similar to GitHub app, when running PR-Agent from BitBucket App, the default [configuration file](pr_agent/settings/configuration.toml) from a pre-built docker 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.
For example, if your local `.pr_agent.toml` file contains:
```
[pr_reviewer]
inline_code_comments = true
```
Each time you invoke a `/review` tool, it will use inline code comments.
#### BitBucket Self-Hosted App automatic tools
You can configure in your local `.pr_agent.toml` file which tools will **run automatically** when a new PR is opened.
Specifically, set the following values:
```yaml
[bitbucket_app]
auto_review = true # set as config var in .pr_agent.toml
auto_describe = true # set as config var in .pr_agent.toml
auto_improve = true # set as config var in .pr_agent.toml
```
`bitbucket_app.auto_review`, `bitbucket_app.auto_describe` and `bitbucket_app.auto_improve` are used to enable/disable automatic tools.
If not set, the default option is that only the `review` tool will run automatically when a new PR is opened.
Note that due to limitations of the bitbucket platform, the `auto_describe` tool will be able to publish a PR description only as a comment.
In addition, some subsections like `PR changes walkthrough` will not appear, since they require the usage of collapsible sections, which are not supported by bitbucket.
### Azure DevOps provider
To use Azure DevOps provider use the following settings in configuration.toml:
```
[config]
git_provider="azure"
use_repo_settings_file=false
```
Azure DevOps provider supports [PAT token](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows) or [DefaultAzureCredential](https://learn.microsoft.com/en-us/azure/developer/python/sdk/authentication-overview#authentication-in-server-environments) authentication.
PAT is faster to create, but has build in experation date, and will use the user identity for API calls.
Using DefaultAzureCredential you can use managed identity or Service principle, which are more secure and will create seperate ADO user identity (via AAD) to the agent.
If PAT was choosen, you can assign the value in .secrets.toml.
If DefaultAzureCredential was choosen, you can assigned the additional env vars like AZURE_CLIENT_SECRET directly,
or use managed identity/az cli (for local develpment) without any additional configuration.
in any case, 'org' value must be assigned in .secrets.toml:
```
[azure_devops]
org = "https://dev.azure.com/YOUR_ORGANIZATION/"
# pat = "YOUR_PAT_TOKEN" needed only if using PAT for authentication
```
##### Azure DevOps Webhook
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 /<command> <args> comment on the relevant PR. Note that for the "Pull request commented on" trigger, only API v2.0 is supported.
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:
```
[azure_devops_server]
pr_commands = [
"/describe --pr_description.add_original_user_description=true --pr_description.keep_original_user_title=true",
"/review --pr_reviewer.num_code_suggestions=0",
"/improve",
]
```
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:
```
[azure_devops_server]
webhook_username = "<basic auth user>"
webhook_password = "<basic auth password>"
```
> :warning: **Ensure that the webhook endpoint is only accessible over HTTPS** to mitigate the risk of credential interception when using basic authentication.
## Appendix - additional configurations walkthrough
#### Ignoring files from analysis
In some cases, you may want to exclude specific files or directories from the analysis performed by CodiumAI PR-Agent. This can be useful, for example, when you have files that are generated automatically or files that shouldn't be reviewed, like vendored code.
To ignore files or directories, edit the **[ignore.toml](/pr_agent/settings/ignore.toml)** configuration file. This setting also exposes the following environment variables:
- `IGNORE.GLOB`
- `IGNORE.REGEX`
For example, 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, set in a configuration file:
```
[ignore]
glob = ['*.py']
```
#### Extra instructions
All PR-Agent 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 ..."
```
#### 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)
#### 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 in your `.secrets.toml` (working from CLI), or in the GitHub `Settings > Secrets and variables` (working from GitHub App or GitHub Action):
```
[openai]
key = "" # your azure api key
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://<your resource name>.openai.azure.com"
deployment_id = "" # The deployment name you chose when you deployed the engine
```
and set in your configuration file:
```
[config]
model="" # the OpenAI model you've deployed on Azure (e.g. gpt-3.5-turbo)
```
##### Huggingface
**Local**
You can run Huggingface 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 Huggingface model locally via Ollama, set:
```
[__init__.py]
MAX_TOKENS = {
"model-name-on-ollama": <max_tokens>
}
e.g.
MAX_TOKENS={
...,
"ollama/llama2": 4096
}
[config] # in configuration.toml
model = "ollama/llama2"
[ollama] # in .secrets.toml
api_base = ... # the base url for your huggingface inference endpoint
# e.g. if running Ollama locally, you may use:
api_base = "http://localhost:11434/"
```
**Inference Endpoints**
To use a new model with Huggingface Inference Endpoints, for example, set:
```
[__init__.py]
MAX_TOKENS = {
"model-name-on-huggingface": <max_tokens>
}
e.g.
MAX_TOKENS={
...,
"meta-llama/Llama-2-7b-chat-hf": 4096
}
[config] # in configuration.toml
model = "huggingface/meta-llama/Llama-2-7b-chat-hf"
[huggingface] # in .secrets.toml
key = ... # your huggingface api key
api_base = ... # the base url for your huggingface 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:
```
[config] # in configuration.toml
model = "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))
Also review the [AiHandler](pr_agent/algo/ai_handler.py) file for instruction how to set keys for other models.
##### Vertex AI
To use Google's Vertex AI platform and its associated models (chat-bison/codechat-bison) set:
```
[config] # in configuration.toml
model = "vertex_ai/codechat-bison"
fallback_models="vertex_ai/codechat-bison"
[vertexai] # in .secrets.toml
vertex_project = "my-google-cloud-project"
vertex_location = ""
```
Your [application default credentials](https://cloud.google.com/docs/authentication/application-default-credentials) will be used for authentication so there is no need to set explicit credentials in most environments.
If you do want to set explicit credentials then you can use the `GOOGLE_APPLICATION_CREDENTIALS` environment variable set to a path to a json credentials file.
##### Amazon Bedrock
To use Amazon Bedrock and its foundational models, add the below configuration:
```
[config] # in configuration.toml
model = "anthropic.claude-v2"
fallback_models="anthropic.claude-instant-v1"
[aws] # in .secrets.toml
bedrock_region = "us-east-1"
```
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.
AWS session is automatically authenticated from your environment, but you can also explicitly set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables.
#### Patch Extra Lines
By default, around any change in your PR, git patch provides 3 lines of context above and below the change.
```
@@ -12,5 +12,5 @@ def func1():
code line that already existed in the file...
code line that already existed in the file...
code line that already existed in the file....
-code line that was removed in the PR
+new code line added in the PR
code line that already existed in the file...
code line that already existed in the file...
code line that already existed in the file...
```
For the `review`, `describe`, `ask` and `add_docs` tools, if the token budget allows, PR-Agent tries to increase the number of lines of context, via the parameter:
```
[config]
patch_extra_lines=3
```
Increasing this number provides more context to the model, but will also increase the token budget.
If the PR is too large (see [PR Compression strategy](./PR_COMPRESSION.md)), PR-Agent automatically sets this number to 0, using the original git patch.
#### 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.
For example, if you want to edit the prompts of the [describe](./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](./pr_agent/tools/pr_description.py#L137).

8
action.yaml Normal file
View File

@ -0,0 +1,8 @@
name: 'Codium PR Agent'
description: 'Summarize, review and suggest improvements for pull requests'
branding:
icon: 'award'
color: 'green'
runs:
using: 'docker'
image: 'Dockerfile.github_action_dockerhub'

View File

@ -1,20 +1,41 @@
FROM python:3.10 as base
WORKDIR /app
ADD pyproject.toml .
ADD requirements.txt .
RUN pip install -r requirements.txt && rm requirements.txt
RUN pip install . && rm pyproject.toml requirements.txt
ENV PYTHONPATH=/app
ADD pr_agent pr_agent
FROM base as github_app
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/github_app.py"]
FROM base as bitbucket_app
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/bitbucket_app.py"]
FROM base as bitbucket_server_webhook
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/bitbucket_server_webhook.py"]
FROM base as github_polling
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/github_polling.py"]
FROM base as gitlab_webhook
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/gitlab_webhook.py"]
FROM base as azure_devops_webhook
ADD pr_agent pr_agent
CMD ["python", "pr_agent/servers/azuredevops_server_webhook.py"]
FROM base as test
ADD requirements-dev.txt .
RUN pip install -r requirements-dev.txt && rm requirements-dev.txt
ADD pr_agent pr_agent
ADD tests tests
FROM base as cli
ADD pr_agent pr_agent
ENTRYPOINT ["python", "pr_agent/cli.py"]

12
docker/Dockerfile.lambda Normal file
View File

@ -0,0 +1,12 @@
FROM public.ecr.aws/lambda/python:3.10
RUN yum update -y && \
yum install -y gcc python3-devel && \
yum clean all
ADD pyproject.toml .
RUN pip install . && rm pyproject.toml
RUN pip install mangum==0.17.0
COPY pr_agent/ ${LAMBDA_TASK_ROOT}/pr_agent/
CMD ["pr_agent.servers.serverless.serverless"]

24
docs/ADD_DOCUMENTATION.md Normal file
View File

@ -0,0 +1,24 @@
# Add Documentation Tool 💎
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
```
For example:
<kbd><img src=https://codium.ai/images/pr_agent/docs_command.png width="768"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/docs_components.png width="768"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/docs_single_component.png width="768"></kbd>
### 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 ...".
Notes
- Language that are currently fully supported: Python, Java, C++, JavaScript, TypeScript.
- For languages that are not fully supported, the tool will suggest documentation only for new components in the PR.
- A previous version of the tool, that offered support only for new components, was deprecated.

24
docs/ASK.md Normal file
View File

@ -0,0 +1,24 @@
# ASK Tool
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 "..."
```
For example:
___
<kbd><img src="https://codium.ai/images/pr_agent/ask_comment.png" width="768"></kbd>
___
<kbd><img src="https://codium.ai/images/pr_agent/ask.png" width="768"></kbd>
___
## 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.
<kbd><img src="https://codium.ai/images/pr_agent/Ask_line.png" width="768"></kbd>
Note that the tool does not have "memory" of previous questions, and answers each question independently.

21
docs/Analyze.md Normal file
View File

@ -0,0 +1,21 @@
# Analyze Tool 💎
The `analyze` tool combines static code analysis with LLM capabilities to provide a comprehensive analysis of the PR code changes.
The tool scans the PR code changes, find the code components (methods, functions, classes) that changed, and summarizes the changes in each component.
It can be invoked manually by commenting on any PR:
```
/analyze
```
An example [result](https://github.com/Codium-ai/pr-agent/pull/546#issuecomment-1868524805):
<kbd><img src=https://codium.ai/images/pr_agent/analyze_1.png width="768"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/analyze_2.png width="768"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/analyze_3.png width="768"></kbd>
Notes
- Language that are currently supported: Python, Java, C++, JavaScript, TypeScript.

31
docs/CI_FEEDBACK.md Normal file
View File

@ -0,0 +1,31 @@
# CI Feedback Tool
The CI feedback tool (`/checks)` automatically triggers when a PR has a failed check.
The tool analyzes the failed checks and provides several feedbacks:
- Failed stage
- Failed test name
- Failure summary
- Relevant error logs
<kbd>
<img src="https://www.codium.ai/images/pr_agent/failed_check1.png" width="768">
</kbd>
&rarr;
<kbd>
<img src="https://www.codium.ai/images/pr_agent/failed_check2.png" width="768">
</kbd>
___
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.
### 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.
- `enable_help_text=true` - if set to true, the tool will provide a help message when a user comments "/checks" on a PR. Default is true.

View File

@ -0,0 +1,65 @@
# Custom Suggestions Tool 💎
## Table of Contents
- [Overview](#overview)
- [Example usage](#example-usage)
- [Configuration options](#configuration-options)
## Overview
The `custom_suggestions` tool scans the PR code changes, and automatically generates custom suggestions for improving the PR code.
It shares similarities with the `improve` tool, but with one main difference: the `custom_suggestions` tool will only propose suggestions that follow specific guidelines defined by the prompt in: `pr_custom_suggestions.prompt` configuration.
The tool can be triggered [automatically](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) every time a new PR is opened, or can be invoked manually by commenting on a PR.
When commenting, use the following template:
```
/custom_suggestions --pr_custom_suggestions.prompt="The suggestions should focus only on the following:\n-...\n-...\n-..."
```
With a [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#working-with-github-app), use the following template:
```
[pr_custom_suggestions]
prompt="""\
The suggestions should focus only on the following:
-...
-...
-...
"""
```
Using a configuration file is recommended, since it allows to use multi-line instructions.
Don't forget - with this tool, you are the prompter. Be specific, clear, and concise in the instructions. Specify relevant aspects that you want the model to focus on. \
You might benefit from several trial-and-error iterations, until you get the correct prompt for your use case.
## Example usage
Here is an example of a possible prompt:
```
[pr_custom_suggestions]
prompt="""\
The suggestions should focus only on the following:
- look for edge cases when implementing a new function
- make sure every variable has a meaningful name
- make sure the code is efficient
"""
```
The instructions above are just an example. We want to emphasize that the prompt should be specific and clear, and be tailored to the needs of your project.
Results obtained with the prompt above:
___
<kbd><img src=https://codium.ai/images/pr_agent/custom_suggestions_prompt.png width="512"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/custom_suggestions_result.png width="768"></kbd>
___
## Configuration options
`prompt`: the prompt for the tool. It should be a multi-line string.
`num_code_suggestions`: number of code suggestions provided by the 'custom_suggestions' tool. Default is 4.
`enable_help_text`: if set to true, the tool will display a help text in the comment. Default is true.

163
docs/DESCRIBE.md Normal file
View File

@ -0,0 +1,163 @@
# Describe Tool
## Table of Contents
- [Overview](#overview)
- [Configuration options](#configuration-options)
- [Inline file summary 💎](#inline-file-summary-)
- [Handle custom labels from the Repo's labels page :gem:](#handle-custom-labels-from-the-repos-labels-page-gem)
- [Markers template](#markers-template)
- [Usage Tips](#usage-tips)
- [Automation](#automation)
- [Custom labels](#custom-labels)
## 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](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools), or it can be invoked manually by commenting on any PR:
```
/describe
```
For example:
___
<kbd><img src=https://codium.ai/images/pr_agent/describe_comment.png width="768"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/describe_new.png width="768"></kbd>
___
### Configuration options
To edit [configurations](./../pr_agent/settings/configuration.toml#L46) related to the describe tool (`pr_description` section), use the following template:
```
/describe --pr_description.some_config1=... --pr_description.some_config2=...
```
**Possible configurations:**
- `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 true.
- `keep_original_user_title`: if set to true, the tool will keep the original PR title, and won't change it. Default is true.
- `extra_instructions`: Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ...".
- To enable `custom labels`, apply the configuration changes described [here](./GENERATE_CUSTOM_LABELS.md#configuration-changes)
- `enable_pr_type`: if set to false, it will not show the `PR type` as a text value in the description content. Default is true.
- `final_update_message`: if set to true, it will add a comment message [`PR Description updated to latest commit...`](https://github.com/Codium-ai/pr-agent/pull/499#issuecomment-1837412176) after finishing calling `/describe`. Default is true.
- `enable_semantic_files_types`: if set to true, "Changes walkthrough" section will be generated. Default is true.
- `collapsible_file_list`: if set to true, the file list in the "Changes walkthrough" section will be collapsible. If set to "adaptive", the file list will be collapsible only if there are more than 8 files. Default is "adaptive".
### Inline file summary 💎
> This feature is available only in PR-Agent Pro
This feature will enable you to quickly understand the changes in each file while reviewing the code changes (diff view).
To add the walkthrough table to the "Files changed" tab, you can click on the checkbox that appears PR Description status message below the main PR Description:
<kbd><img src=https://codium.ai/images/pr_agent/add_table_checkbox.png width="512"></kbd>
If you prefer to have the file summaries appear in the "Files changed" tab on every PR, change the `pr_description.inline_file_summary` parameter in the configuration file, possible values are:
- `'table'`: File changes walkthrough table will be displayed on the top of the "Files changed" tab, in addition to the "Conversation" tab.
<kbd><img src=https://codium.ai/images/pr_agent/diffview-table.png width="768"></kbd>
- `true`: A collapsable file comment with changes title and a changes summary for each file in the PR.
<kbd><img src=https://codium.ai/images/pr_agent/diffview_changes.png width="768"></kbd>
- `false` (`default`): File changes walkthrough will be added only to the "Conversation" tab.
Note that this feature is currently available only for GitHub.
### Handle custom labels from the Repo's labels page :gem:
> This feature is available only in PR-Agent Pro
You can 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)
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`.<br>
The description should be comprehensive and detailed, indicating when to add the desired label. For example:
<kbd><img src=https://codium.ai/images/pr_agent/add_native_custom_labels.png width="880"></kbd>
### 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...
## PR Type:
pr_agent:type
## PR Description:
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.
<kbd><img src=https://codium.ai/images/pr_agent/describe_markers_before.png width="768"></kbd>
==>
<kbd><img src=https://codium.ai/images/pr_agent/describe_markers_after.png width="768"></kbd>
**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.
## Usage Tips
1) [Automation](#automation)
2) [Custom labels](#custom-labels)
### Automation
- When you first install the app, the [default mode](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) for the describe tool is:
```
pr_commands = ["/describe --pr_description.add_original_user_description=true"
"--pr_description.keep_original_user_title=true", ...]
```
meaning the `describe` tool will run automatically on every PR, will keep the original title, and will add the original user description above the generated description.
<br> This default settings aim to strike a good balance between automation and control:
If you want more automation, just give the PR a title, and the tool will auto-write a full description; If you want more control, you can add a detailed description, and the tool will add the complementary description below it.
- For maximal automation, you can change the default mode to:
```
pr_commands = ["/describe --pr_description.add_original_user_description=false"
"--pr_description.keep_original_user_title=true", ...]
```
so the title will be auto-generated as well.
- 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.
- `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.
### 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`].
If you specify [custom labels](#handle-custom-labels-from-the-repos-labels-page-gem) in the repo's labels page, you can get tailored labels for your use cases.
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
- ...
The list above is eclectic, and aims to give an idea of different possibilities. Define custom labels that are relevant for your repo and use cases.
Note that Labels are not mutually exclusive, so you can add multiple label categories.
<br>Make sure to provide proper title, and a detailed and well-phrased description for each label, so the tool will know when to suggest it.

27
docs/Full_environments.md Normal file
View File

@ -0,0 +1,27 @@
## Overview
`PR-Agent` offers extensive pull request functionalities across various git providers:
| | | 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: |
| | ⮑ Extended | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
| | Reflect and Review | :white_check_mark: | :white_check_mark: | :white_check_mark: | | :white_check_mark: | :white_check_mark: |
| | Update CHANGELOG.md | :white_check_mark: | :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: |
| | Generate Custom Labels 💎 | :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: | | | :white_check_mark: |
| | Tagging bot | :white_check_mark: | | | | |
| | Actions | :white_check_mark: | | | | |
| | Web server | | | | | | :white_check_mark: |
| | | | | | | |
| CORE | PR compression | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Repo language prioritization | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | Adaptive and token-aware<br />file patch fitting | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| | 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: | | | | | |

View File

@ -0,0 +1,57 @@
# Generate Custom Labels 💎
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
```
For example:
If we wish to add detect changes to SQL queries in a given PR, we can add the following custom label along with its description:
<kbd><img src=https://codium.ai/images/pr_agent/custom_labels_list.png width="768"></kbd>
When running the `generate_labels` tool on a PR that includes changes in SQL queries, it will automatically suggest the custom label:
<kbd><img src=https://codium.ai/images/pr_agent/custom_label_published.png width="768"></kbd>
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-changes) to the [custom_labels file](./../pr_agent/settings/custom_labels.toml):
#### 2. Repo configuration file
To enable custom labels, you need to apply the [configuration changes](#configuration-changes) to the local `.pr_agent.toml` file in you repository.
#### 3. Handle custom labels from the Repo's labels page
> This feature is available only in PR-Agent Pro
* 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`.<br>
The description should be comprehensive and detailed, indicating when to add the desired label.
<kbd><img src=https://codium.ai/images/pr_agent/add_native_custom_labels.png width="880"></kbd>
c. Now the custom labels will be included in the `generate_labels` tool.
*This feature is supported in GitHub and GitLab.
#### Configuration changes
- 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]
enable_custom_labels=true
[custom_labels."Custom Label Name"]
description = "Description of when AI should suggest this label"
[custom_labels."Custom Label 2"]
description = "Description of when AI should suggest this label 2"
```

97
docs/IMPROVE.md Normal file
View File

@ -0,0 +1,97 @@
# Improve Tool
## Table of Contents
- [Overview](#overview)
- [Configuration options](#configuration-options)
- [Usage Tips](#usage-tips)
- [Extra instructions](#extra-instructions)
- [PR footprint - regular vs summarize mode](#pr-footprint---regular-vs-summarize-mode)
- [A note on code suggestions quality](#a-note-on-code-suggestions-quality)
## Overview
The `improve` tool scans the PR code changes, and automatically generates suggestions for improving the PR code.
The tool can be triggered automatically every time a new PR is [opened](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools), or it can be invoked manually by commenting on any PR:
```
/improve
```
### Summarized vs committable code suggestions
The code suggestions can be presented as a single comment (via `pr_code_suggestions.summarize=true`):
___
<kbd><img src=https://codium.ai/images/pr_agent/code_suggestions_as_comment.png width="768"></kbd>
___
Or as a separate commitable code comment for each suggestion:
___
<kbd><img src=https://codium.ai/images/pr_agent/improve.png width="768"></kbd>
---
Note that a single 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 are presented there as code comments.
### Extended mode
An extended mode, which does not involve PR Compression and provides more comprehensive suggestions, can be invoked by commenting on any PR:
```
/improve --extended
```
or by setting:
```
[pr_code_suggestions]
auto_extended_mode=true
```
(True by default).
Note that the extended mode divides the PR code changes into chunks, up to the token limits, where each chunk is handled separately (might use multiple calls to GPT-4 for large PRs).
Hence, the total number of suggestions is proportional to the number of chunks, i.e., the size of the PR.
### Configuration options
To edit [configurations](./../pr_agent/settings/configuration.toml#L66) related to the improve tool (`pr_code_suggestions` section), use the following template:
```
/improve --pr_code_suggestions.some_config1=... --pr_code_suggestions.some_config2=...
```
#### General options
- `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.
- `summarize`: if set to true, the tool will display the suggestions in a single comment. Default is false.
- `enable_help_text`: if set to true, the tool will display a help text in the comment. Default is true.
#### params for '/improve --extended' mode
- `auto_extended_mode`: enable extended mode automatically (no need for the `--extended` option). Default is true.
- `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.
## Usage Tips
### Extra instructions
Extra instructions are very important for the `imrpove` tool, since they enable you to guide the model to suggestions that are more relevant to the specific needs of the project.
Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify relevant aspects that you want the model to focus on.
Examples for extra instructions:
```
[pr_code_suggestions] # /improve #
extra_instructions="""
Emphasize the following aspects:
- Does the code logic cover relevant edge cases?
- Is the code logic clear and easy to understand?
- Is the code logic efficient?
...
"""
```
Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.
### A note on code suggestions quality
- While the current AI for code is getting better and better (GPT-4), it's not flawless. Not all the suggestions will be perfect, and a user should not accept all of them automatically.
- Suggestions are not meant to be [simplistic](./../pr_agent/settings/pr_code_suggestions_prompts.toml#L34). Instead, they aim to give deep feedback and raise questions, ideas and thoughts to the user, who can then use his judgment, experience, and understanding of the code base.
- Recommended to use the `exra_instructions` field to guide the model to suggestions that are more relevant to the specific needs of the project.
- Consider also trying the [Custom Suggestions Tool](https://github.com/Codium-ai/pr-agent/blob/main/docs/CUSTOM_SUGGESTIONS.md) 💎, that will **only** propose suggestions that follow specific guidelines defined by user.

176
docs/REVIEW.md Normal file
View File

@ -0,0 +1,176 @@
# Review Tool
## Table of Contents
- [Overview](#overview)
- [Configuration options](#configuration-options)
- [Incremental Mode](#incremental-mode)
- [PR Reflection](#pr-reflection)
- [Usage Tips](#usage-tips)
- [General guidelines](#general-guidelines)
- [Code suggestions](#code-suggestions)
- [Automation](#automation)
- [Auto-labels](#auto-labels)
- [Extra instructions](#extra-instructions)
- [Auto-approval](#auto-approval-1)
## Overview
The `review` tool scans the PR code changes, and automatically generates a PR review.
The tool can be triggered automatically every time a new PR is [opened](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools), or can be invoked manually by commenting on any PR:
```
/review
```
For example:
___
<kbd><img src=https://codium.ai/images/pr_agent/review_comment.png width="768"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/review.png width="768"></kbd>
___
### Configuration options
To edit [configurations](./../pr_agent/settings/configuration.toml#L19) related to the review tool (`pr_reviewer` section), use the following template:
```
/review --pr_reviewer.some_config1=... --pr_reviewer.some_config2=...
```
#### General options
- `num_code_suggestions`: number of code suggestions provided by the 'review' tool. For manual comments, default is 4. For [PR-Agent app](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L142) auto tools, default is 0, meaning no code suggestions will be provided by the review tool, unless you manually edit `pr_commands`.
- `inline_code_comments`: if set to true, the tool will publish the code suggestions as comments on the code diff. Default is false.
- `persistent_comment`: if set to true, the review comment will be persistent, meaning that every new review request will edit the previous one. Default is true.
- `extra_instructions`: Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ...".
#### Enable\\disable features
- `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_estimate_effort_to_review`: if set to true, the tool will add a section that estimates the effort needed to review the PR. Default is true.
#### SOC2 ticket compliance 💎
This sub-tool checks if the PR description properly contains a ticket to a project management system (e.g., Jira, Asana, Trello, etc.), as required by SOC2 compliance. If not, it will add a label to the PR: "Missing SOC2 ticket".
- `require_soc2_ticket`: If set to true, the SOC2 ticket checker sub-tool will be enabled. Default is false.
- `soc2_ticket_prompt`: The prompt for the SOC2 ticket review. Default is: `Does the PR description include a link to ticket in a project management system (e.g., Jira, Asana, Trello, etc.) ?`. Edit this field if your compliance requirements are different.
#### Adding PR labels
- `enable_review_labels_security`: if set to true, the tool will publish a 'possible security issue' label if it detects a security issue. Default is true.
- `enable_review_labels_effort`: if set to true, the tool will publish a 'Review effort [1-5]: x' label. Default is true.
#### Auto-approval
- `enable_auto_approval`: if set to true, the tool will approve the PR when invoked with the 'auto_approve' command. Default is false. This flag can be changed only from configuration file.
- `maximal_review_effort`: maximal effort level for auto-approval. If the PR's estimated review effort is above this threshold, the auto-approval will not run. Default is 5.
### Incremental Mode
Incremental review 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.
For invoking the incremental mode, the following command can be used:
```
/review -i
```
Note that the incremental mode is only available for GitHub.
<kbd><img src=https://codium.ai/images/pr_agent/incremental_review.png width="768"></kbd>
Under the section 'pr_reviewer', the [configuration file](./../pr_agent/settings/configuration.toml#L19) contains options to customize the 'review -i' tool.
These configurations can be used to control the rate at which the incremental review tool will create new review comments when invoked automatically, to prevent making too much noise in the PR.
- `minimal_commits_for_incremental_review`: Minimal number of commits since the last review that are required to create incremental review.
If there are less than the specified number of commits since the last review, the tool will not perform any action.
Default is 0 - the tool will always run, no matter how many commits since the last review.
- `minimal_minutes_for_incremental_review`: Minimal number of minutes that need to pass since the last reviewed commit to create incremental review.
If less than the specified number of minutes have passed between the last reviewed commit and running this command, the tool will not perform any action.
Default is 0 - the tool will always run, no matter how much time have passed since the last reviewed commit.
- `require_all_thresholds_for_incremental_review`: If set to true, all the previous thresholds must be met for incremental review to run. If false, only one is enough to run the tool.
For example, if `minimal_commits_for_incremental_review=2` and `minimal_minutes_for_incremental_review=2`, and we have 3 commits since the last review, but the last reviewed commit is from 1 minute ago:
When `require_all_thresholds_for_incremental_review=true` the incremental review __will not__ run, because only 1 out of 2 conditions were met (we have enough commits but the last review is too recent),
but when `require_all_thresholds_for_incremental_review=false` the incremental review __will__ run, because one condition is enough (we have 3 commits which is more than the configured 2).
Default is false - the tool will run as long as at least once conditions is met.
### 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 their answers.
<kbd><img src=https://codium.ai/images/pr_agent/reflection_questions.png width="768"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/reflection_answers.png width="768"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/reflection_insights.png width="768"></kbd>
___
## Usage Tips
1) [General guidelines](#general-guidelines)
2) [Code suggestions](#code-suggestions)
3) [Automation](#automation)
4) [Auto-labels](#auto-labels)
5) [Extra instructions](#extra-instructions)
6) [Auto-approval](#auto-approval)
### General guidelines
The `review` tool provides a collection of possible feedbacks about a PR.
It is recommended to review the [Configuration options](#configuration-options) section, and choose the relevant options for your use case.
Some of the features that are disabled by default are quite useful, and should be considered for enabling. For example:
`require_score_review`, `require_soc2_ticket`, and more.
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.
### Code suggestions
If you set `num_code_suggestions`>0 , the `review` tool will also provide code suggestions.
Notice If you are interested **only** in the code suggestions, it is recommended to use the [`improve`](./IMPROVE.md) feature instead, since it is a dedicated only to code suggestions, and usually gives better results.
Use the `review` tool if you want to get more comprehensive feedback, which includes code suggestions as well.
### Automation
- When you first install the app, the [default mode](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) for the `review` tool is:
```
pr_commands = ["/review", ...]
```
Meaning the `review` tool will run automatically on every PR, with the default configuration.
Edit this field to enable/disable the tool, or to change the used configurations.
### Auto-labels
The `review` tool can auto-generate two specific types of labels for a PR:
- a `possible security issue` label that detects a possible [security issue](https://github.com/Codium-ai/pr-agent/blob/tr/user_description/pr_agent/settings/pr_reviewer_prompts.toml#L136) (`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)
Both modes are useful, and we recommended to enable them.
### Extra instructions
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.
Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify the relevant sub-tool, and the relevant aspects of the PR that you want to emphasize.
Examples for extra instructions:
```
[pr_reviewer] # /review #
extra_instructions="""
In the code feedback section, emphasize the following:
- Does the code logic cover relevant edge cases?
- Is the code logic clear and easy to understand?
- Is the code logic efficient?
...
"""
```
Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.
### Auto-approval
PR-Agent can approve a PR when a specific comment is invoked.
To ensure safety, the auto-approval feature is disabled by default. To enable auto-approval, you need to actively set in a pre-defined configuration file the following:
```
[pr_reviewer]
enable_auto_approval = true
```
(this specific flag cannot be set with a command line argument, only in the configuration file, committed to the repository)
After enabling, by commenting on a PR:
```
/review auto_approve
```
PR-Agent will automatically approve the PR, and add a comment with the approval.
You can also enable auto-approval only if the PR meets certain requirements, such as that the `estimated_review_effort` label is equal or below a certain threshold, by adjusting the flag:
```
[pr_reviewer]
maximal_review_effort = 5
```

39
docs/SIMILAR_ISSUE.md Normal file
View File

@ -0,0 +1,39 @@
# 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:
<kbd><img src=https://codium.ai/images/pr_agent/similar_issue_original_issue.png width="768"></kbd>
<kbd><img src=https://codium.ai/images/pr_agent/similar_issue_comment.png width="768"></kbd>
<kbd><img src=https://codium.ai/images/pr_agent/similar_issue.png width="768"></kbd>
Note that to perform retrieval, the `similar_issue` tool indexes all the repo previous issues (once).
**Select VectorDBs** by changing `pr_similar_issue` parameter in `configuration.toml` file
2 VectorDBs are available to switch in
1. LanceDB
2. Pinecone
To enable usage of the '**similar issue**' tool for Pinecone, 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)

30
docs/TEST.md Normal file
View File

@ -0,0 +1,30 @@
# Test Tool 💎
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, use the [`analyze`](https://github.com/Codium-ai/pr-agent/blob/main/docs/Analyze.md) tool.
An example [result](https://github.com/Codium-ai/pr-agent/pull/598#issuecomment-1913679429):
<kbd><img src=https://codium.ai/images/pr_agent/test1.png width="704"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/test2.png width="768"></kbd>
___
<kbd><img src=https://codium.ai/images/pr_agent/test3.png width="768"></kbd>
Language that are currently supported by the tool: Python, Java, C++, JavaScript, TypeScript.
### 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.
- `extra_instructions`: Optional extra instructions to the tool. For example: "use the following mock injection scheme: ...".
- `file`: in case there are several components with the same name, you can specify the relevant file.
- `class_name`: in case there are several methods with the same name in the same file, you can specify the relevant class name.
- `enable_help_text`: if set to true, the tool will add a help text to the PR comment. Default is true.

15
docs/TOOLS_GUIDE.md Normal file
View File

@ -0,0 +1,15 @@
## Tools Guide
- [DESCRIBE](./DESCRIBE.md)
- [REVIEW](./REVIEW.md)
- [IMPROVE](./IMPROVE.md)
- [ASK](./ASK.md)
- [SIMILAR_ISSUE](./SIMILAR_ISSUE.md)
- [UPDATE CHANGELOG](./UPDATE_CHANGELOG.md)
- [CUSTOM SUGGESTIONS](./CUSTOM_SUGGESTIONS.md) 💎
- [ADD DOCUMENTATION](./ADD_DOCUMENTATION.md) 💎
- [GENERATE CUSTOM LABELS](./GENERATE_CUSTOM_LABELS.md) 💎
- [Analyze](./Analyze.md) 💎
- [Test](./TEST.md) 💎
- [CI Feedback](./CI_FEEDBACK.md) 💎
See the **[installation guide](/INSTALL.md)** for instructions on setting up PR-Agent.

19
docs/UPDATE_CHANGELOG.md Normal file
View File

@ -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:
<kbd><img src=https://codium.ai/images/pr_agent/update_changelog_comment.png width="768"></kbd>
<kbd><img src=https://codium.ai/images/pr_agent/update_changelog.png width="768"></kbd>
### 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 ...

View File

@ -0,0 +1,2 @@
#!/bin/bash
python /app/pr_agent/servers/github_action_runner.py

BIN
pics/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

View File

@ -1,24 +1,94 @@
import re
import shlex
from functools import partial
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.utils import update_settings_from_args
from pr_agent.config_loader import get_settings
from pr_agent.git_providers.utils import apply_repo_settings
from pr_agent.log import get_logger
from pr_agent.tools.pr_add_docs import PRAddDocs
from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions
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_message import PRHelpMessage
from pr_agent.tools.pr_information_from_user import PRInformationFromUser
from pr_agent.tools.pr_line_questions import PR_LineQuestions
from pr_agent.tools.pr_questions import PRQuestions
from pr_agent.tools.pr_reviewer import PRReviewer
from pr_agent.tools.pr_similar_issue import PRSimilarIssue
from pr_agent.tools.pr_update_changelog import PRUpdateChangelog
command2class = {
"auto_review": PRReviewer,
"answer": PRReviewer,
"review": PRReviewer,
"review_pr": PRReviewer,
"reflect": PRInformationFromUser,
"reflect_and_review": PRInformationFromUser,
"describe": PRDescription,
"describe_pr": PRDescription,
"improve": PRCodeSuggestions,
"improve_code": PRCodeSuggestions,
"ask": PRQuestions,
"ask_question": PRQuestions,
"ask_line": PR_LineQuestions,
"update_changelog": PRUpdateChangelog,
"config": PRConfig,
"settings": PRConfig,
"help": PRHelpMessage,
"similar_issue": PRSimilarIssue,
"add_docs": PRAddDocs,
"generate_labels": PRGenerateLabels,
}
commands = list(command2class.keys())
class PRAgent:
def __init__(self):
pass
def __init__(self, ai_handler: partial[BaseAiHandler,] = LiteLLMAIHandler):
self.ai_handler = ai_handler # will be initialized in run_action
self.forbidden_cli_args = ['enable_auto_approval']
async def handle_request(self, pr_url, request):
if 'please review' in request.lower() or 'review' == request.lower().strip() or len(request) == 0:
reviewer = PRReviewer(pr_url)
await reviewer.review()
async def handle_request(self, pr_url, request, notify=None) -> bool:
# First, apply repo specific settings if exists
apply_repo_settings(pr_url)
# Then, apply user specific settings if exists
if isinstance(request, str):
request = request.replace("'", "\\'")
lexer = shlex.shlex(request, posix=True)
lexer.whitespace_split = True
action, *args = list(lexer)
else:
if "please answer" in request.lower():
question = re.split(r'(?i)please answer', request)[1].strip()
elif request.lower().strip().startswith("answer"):
question = re.split(r'(?i)answer', request)[1].strip()
action, *args = request
if args:
for forbidden_arg in self.forbidden_cli_args:
for arg in args:
if forbidden_arg in arg:
get_logger().error(f"CLI argument for param '{forbidden_arg}' is forbidden. Use instead a configuration file.")
return False
args = update_settings_from_args(args)
action = action.lstrip("/").lower()
with get_logger().contextualize(command=action):
get_logger().info("PR-Agent request handler started", analytics=True)
if action == "reflect_and_review":
get_settings().pr_reviewer.ask_and_reflect = True
if action == "answer":
if notify:
notify()
await PRReviewer(pr_url, is_answer=True, args=args, ai_handler=self.ai_handler).run()
elif action == "auto_review":
await PRReviewer(pr_url, is_auto=True, args=args, ai_handler=self.ai_handler).run()
elif action in command2class:
if notify:
notify()
await command2class[action](pr_url, ai_handler=self.ai_handler, args=args).run()
else:
question = request
answerer = PRQuestions(pr_url, question)
await answerer.answer()
return False
return True

View File

@ -1,4 +1,5 @@
MAX_TOKENS = {
'text-embedding-ada-002': 8000,
'gpt-3.5-turbo': 4000,
'gpt-3.5-turbo-0613': 4000,
'gpt-3.5-turbo-0301': 4000,
@ -7,4 +8,18 @@ MAX_TOKENS = {
'gpt-4': 8000,
'gpt-4-0613': 8000,
'gpt-4-32k': 32000,
'gpt-4-1106-preview': 128000, # 128K, but may be limited by config.max_model_tokens
'gpt-4-0125-preview': 128000, # 128K, but may be limited by config.max_model_tokens
'claude-instant-1': 100000,
'claude-2': 100000,
'command-nightly': 4096,
'replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1': 4096,
'meta-llama/Llama-2-7b-chat-hf': 4096,
'vertex_ai/codechat-bison': 6144,
'vertex_ai/codechat-bison-32k': 32000,
'codechat-bison': 6144,
'codechat-bison-32k': 32000,
'anthropic.claude-v2': 100000,
'anthropic.claude-instant-v1': 100000,
'anthropic.claude-v1': 100000,
}

View File

@ -1,47 +0,0 @@
import logging
import openai
from openai.error import APIError, Timeout, TryAgain
from retry import retry
from pr_agent.config_loader import settings
OPENAI_RETRIES=2
class AiHandler:
def __init__(self):
try:
openai.api_key = settings.openai.key
if settings.get("OPENAI.ORG", None):
openai.organization = settings.openai.org
self.deployment_id = settings.get("OPENAI.DEPLOYMENT_ID", None)
if settings.get("OPENAI.API_TYPE", None):
openai.api_type = settings.openai.api_type
if settings.get("OPENAI.API_VERSION", None):
openai.engine = settings.openai.api_version
if settings.get("OPENAI.API_BASE", None):
openai.api_base = settings.openai.api_base
except AttributeError as e:
raise ValueError("OpenAI key is required") from e
@retry(exceptions=(APIError, Timeout, TryAgain, AttributeError),
tries=OPENAI_RETRIES, delay=2, backoff=2, jitter=(1, 3))
async def chat_completion(self, model: str, temperature: float, system: str, user: str):
try:
response = await openai.ChatCompletion.acreate(
model=model,
deployment_id=self.deployment_id,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": user}
],
temperature=temperature,
)
except (APIError, Timeout, TryAgain) as e:
logging.error("Error during OpenAI inference: ", e)
raise
if response is None or len(response.choices) == 0:
raise TryAgain
resp = response.choices[0]['message']['content']
finish_reason = response.choices[0].finish_reason
return resp, finish_reason

View File

@ -0,0 +1,28 @@
from abc import ABC, abstractmethod
class BaseAiHandler(ABC):
"""
This class defines the interface for an AI handler to be used by the PR Agents.
"""
@abstractmethod
def __init__(self):
pass
@property
@abstractmethod
def deployment_id(self):
pass
@abstractmethod
async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2):
"""
This method should be implemented to return a chat completion from the AI model.
Args:
model (str): the name of the model to use for the chat completion
system (str): the system message string to use for the chat completion
user (str): the user message string to use for the chat completion
temperature (float): the temperature to use for the chat completion
"""
pass

View File

@ -0,0 +1,67 @@
try:
from langchain.chat_models import ChatOpenAI, AzureChatOpenAI
from langchain.schema import SystemMessage, HumanMessage
except: # we don't enforce langchain as a dependency, so if it's not installed, just move on
pass
from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler
from pr_agent.config_loader import get_settings
from pr_agent.log import get_logger
from openai.error import APIError, RateLimitError, Timeout, TryAgain
from retry import retry
import functools
OPENAI_RETRIES = 5
class LangChainOpenAIHandler(BaseAiHandler):
def __init__(self):
# Initialize OpenAIHandler specific attributes here
super().__init__()
self.azure = get_settings().get("OPENAI.API_TYPE", "").lower() == "azure"
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
self._chat = functools.partial(
lambda **kwargs: AzureChatOpenAI(**kwargs),
openai_api_key=get_settings().openai.key,
openai_api_base=get_settings().openai.api_base,
openai_api_version=get_settings().openai.api_version,
)
else:
self._chat = ChatOpenAI(openai_api_key=get_settings().openai.key)
except AttributeError as e:
if getattr(e, "name"):
raise ValueError(f"OpenAI {e.name} is required") from e
else:
raise e
@property
def chat(self):
if self.azure:
# we must set the deployment_id only here (instead of the __init__ method) to support fallback_deployments
return self._chat(deployment_name=self.deployment_id)
else:
return self._chat
@property
def deployment_id(self):
"""
Returns the deployment ID for the OpenAI API.
"""
return get_settings().get("OPENAI.DEPLOYMENT_ID", None)
@retry(exceptions=(APIError, Timeout, TryAgain, AttributeError, RateLimitError),
tries=OPENAI_RETRIES, delay=2, backoff=2, jitter=(1, 3))
async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2):
try:
messages=[SystemMessage(content=system), HumanMessage(content=user)]
# get a chat completion from the formatted messages
resp = self.chat(messages, model=model, temperature=temperature)
finish_reason="completed"
return resp.content, finish_reason
except (Exception) as e:
get_logger().error("Unknown error during OpenAI inference: ", e)
raise e

View File

@ -0,0 +1,138 @@
import os
import boto3
import litellm
import openai
from litellm import acompletion
from openai.error import APIError, RateLimitError, Timeout, TryAgain
from retry import retry
from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler
from pr_agent.config_loader import get_settings
from pr_agent.log import get_logger
OPENAI_RETRIES = 5
class LiteLLMAIHandler(BaseAiHandler):
"""
This class handles interactions with the OpenAI API for chat completions.
It initializes the API key and other settings from a configuration file,
and provides a method for performing chat completions using the OpenAI ChatCompletion API.
"""
def __init__(self):
"""
Initializes the OpenAI API key and other settings from a configuration file.
Raises a ValueError if the OpenAI key is missing.
"""
self.azure = False
self.aws_bedrock_client = None
if get_settings().get("OPENAI.KEY", None):
openai.api_key = get_settings().openai.key
litellm.openai_key = get_settings().openai.key
if get_settings().get("litellm.use_client"):
litellm_token = get_settings().get("litellm.LITELLM_TOKEN")
assert litellm_token, "LITELLM_TOKEN is required"
os.environ["LITELLM_TOKEN"] = litellm_token
litellm.use_client = True
if get_settings().get("OPENAI.ORG", None):
litellm.organization = get_settings().openai.org
if get_settings().get("OPENAI.API_TYPE", None):
if get_settings().openai.api_type == "azure":
self.azure = True
litellm.azure_key = get_settings().openai.key
if get_settings().get("OPENAI.API_VERSION", None):
litellm.api_version = get_settings().openai.api_version
if get_settings().get("OPENAI.API_BASE", None):
litellm.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):
litellm.cohere_key = get_settings().cohere.key
if get_settings().get("REPLICATE.KEY", None):
litellm.replicate_key = get_settings().replicate.key
if get_settings().get("REPLICATE.KEY", None):
litellm.replicate_key = get_settings().replicate.key
if get_settings().get("HUGGINGFACE.KEY", None):
litellm.huggingface_key = get_settings().huggingface.key
if get_settings().get("HUGGINGFACE.API_BASE", None):
litellm.api_base = get_settings().huggingface.api_base
if get_settings().get("VERTEXAI.VERTEX_PROJECT", None):
litellm.vertex_project = get_settings().vertexai.vertex_project
litellm.vertex_location = get_settings().get(
"VERTEXAI.VERTEX_LOCATION", None
)
if get_settings().get("AWS.BEDROCK_REGION", None):
litellm.AmazonAnthropicConfig.max_tokens_to_sample = 2000
self.aws_bedrock_client = boto3.client(
service_name="bedrock-runtime",
region_name=get_settings().aws.bedrock_region,
)
@property
def deployment_id(self):
"""
Returns the deployment ID for the OpenAI API.
"""
return get_settings().get("OPENAI.DEPLOYMENT_ID", None)
@retry(exceptions=(APIError, Timeout, TryAgain, AttributeError, RateLimitError),
tries=OPENAI_RETRIES, delay=2, backoff=2, jitter=(1, 3))
async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2):
"""
Performs a chat completion using the OpenAI ChatCompletion API.
Retries in case of API errors or timeouts.
Args:
model (str): The model to use for chat completion.
temperature (float): The temperature parameter for chat completion.
system (str): The system message for chat completion.
user (str): The user message for chat completion.
Returns:
tuple: A tuple containing the response and finish reason from the API.
Raises:
TryAgain: If the API response is empty or there are no choices in the response.
APIError: If there is an error during OpenAI inference.
Timeout: If there is a timeout during OpenAI inference.
TryAgain: If there is an attribute error during OpenAI inference.
"""
try:
resp, finish_reason = None, None
deployment_id = self.deployment_id
if self.azure:
model = 'azure/' + model
messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
kwargs = {
"model": model,
"deployment_id": deployment_id,
"messages": messages,
"temperature": temperature,
"force_timeout": get_settings().config.ai_timeout,
}
if self.aws_bedrock_client:
kwargs["aws_bedrock_client"] = self.aws_bedrock_client
get_logger().debug("Prompts", artifact={"system": system, "user": user})
response = await acompletion(**kwargs)
except (APIError, Timeout, TryAgain) as e:
get_logger().error("Error during OpenAI 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)
raise TryAgain from e
if response is None or len(response["choices"]) == 0:
raise TryAgain
else:
resp = response["choices"][0]['message']['content']
finish_reason = response["choices"][0]["finish_reason"]
# usage = response.get("usage")
get_logger().debug(f"\nAI response:\n{resp}")
get_logger().debug("Full_response", artifact=response)
return resp, finish_reason

View File

@ -0,0 +1,67 @@
from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler
import openai
from openai.error import APIError, RateLimitError, Timeout, TryAgain
from retry import retry
from pr_agent.config_loader import get_settings
from pr_agent.log import get_logger
OPENAI_RETRIES = 5
class OpenAIHandler(BaseAiHandler):
def __init__(self):
# Initialize OpenAIHandler specific attributes here
try:
super().__init__()
openai.api_key = get_settings().openai.key
if get_settings().get("OPENAI.ORG", None):
openai.organization = get_settings().openai.org
if get_settings().get("OPENAI.API_TYPE", None):
if get_settings().openai.api_type == "azure":
self.azure = True
openai.azure_key = get_settings().openai.key
if get_settings().get("OPENAI.API_VERSION", None):
openai.api_version = get_settings().openai.api_version
if get_settings().get("OPENAI.API_BASE", None):
openai.api_base = get_settings().openai.api_base
except AttributeError as e:
raise ValueError("OpenAI key is required") from e
@property
def deployment_id(self):
"""
Returns the deployment ID for the OpenAI API.
"""
return get_settings().get("OPENAI.DEPLOYMENT_ID", None)
@retry(exceptions=(APIError, Timeout, TryAgain, AttributeError, RateLimitError),
tries=OPENAI_RETRIES, delay=2, backoff=2, jitter=(1, 3))
async def chat_completion(self, model: str, system: str, user: str, temperature: float = 0.2):
try:
deployment_id = self.deployment_id
get_logger().info("System: ", system)
get_logger().info("User: ", user)
messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
chat_completion = await openai.ChatCompletion.acreate(
model=model,
deployment_id=deployment_id,
messages=messages,
temperature=temperature,
)
resp = chat_completion["choices"][0]['message']['content']
finish_reason = chat_completion["choices"][0]["finish_reason"]
usage = chat_completion.get("usage")
get_logger().info("AI response", response=resp, messages=messages, finish_reason=finish_reason,
model=model, usage=usage)
return resp, finish_reason
except (APIError, Timeout, TryAgain) as e:
get_logger().error("Error during OpenAI 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)
raise TryAgain from e

View File

@ -0,0 +1,36 @@
import fnmatch
import re
from pr_agent.config_loader import get_settings
def filter_ignored(files):
"""
Filter out files that match the ignore patterns.
"""
try:
# load regex patterns, and translate glob patterns to regex
patterns = get_settings().ignore.regex
if isinstance(patterns, str):
patterns = [patterns]
glob_setting = get_settings().ignore.glob
if isinstance(glob_setting, str): # --ignore.glob=[.*utils.py], --ignore.glob=.*utils.py
glob_setting = glob_setting.strip('[]').split(",")
patterns += [fnmatch.translate(glob) for glob in glob_setting]
# compile all valid patterns
compiled_patterns = []
for r in patterns:
try:
compiled_patterns.append(re.compile(r))
except re.error:
pass
# keep filenames that _don't_ match the ignore regex
for r in compiled_patterns:
files = [f for f in files if (f.filename and not r.match(f.filename))]
except Exception as e:
print(f"Could not filter file list: {e}")
return files

View File

@ -1,18 +1,30 @@
from __future__ import annotations
import logging
import re
from pr_agent.config_loader import settings
from pr_agent.config_loader import get_settings
from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
from pr_agent.log import get_logger
def extend_patch(original_file_str, patch_str, num_lines) -> str:
"""
Extends the patch to include 'num_lines' more surrounding lines
Extends the given patch to include a specified number of surrounding lines.
Args:
original_file_str (str): The original file to which the patch will be applied.
patch_str (str): The patch to be applied to the original file.
num_lines (int): The number of surrounding lines to include in the extended patch.
Returns:
str: The extended patch string.
"""
if not patch_str or num_lines == 0:
return patch_str
if type(original_file_str) == bytes:
original_file_str = original_file_str.decode('utf-8')
original_lines = original_file_str.splitlines()
patch_lines = patch_str.splitlines()
extended_patch_lines = []
@ -30,8 +42,16 @@ def extend_patch(original_file_str, patch_str, num_lines) -> str:
extended_patch_lines.extend(
original_lines[start1 + size1 - 1:start1 + size1 - 1 + num_lines])
start1, size1, start2, size2 = map(int, match.groups()[:4])
section_header = match.groups()[4]
res = list(match.groups())
for i in range(len(res)):
if res[i] is None:
res[i] = 0
try:
start1, size1, start2, size2 = map(int, res[:4])
except: # '@@ -0,0 +1 @@' case
start1, size1, size2 = map(int, res[:3])
start2 = 0
section_header = res[4]
extended_start1 = max(1, start1 - num_lines)
extended_size1 = size1 + (start1 - extended_start1) + num_lines
extended_start2 = max(1, start2 - num_lines)
@ -44,8 +64,8 @@ def extend_patch(original_file_str, patch_str, num_lines) -> str:
continue
extended_patch_lines.append(line)
except Exception as e:
if settings.config.verbosity_level >= 2:
logging.error(f"Failed to extend patch: {e}")
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to extend patch: {e}")
return patch_str
# finish previous hunk
@ -58,6 +78,14 @@ def extend_patch(original_file_str, patch_str, num_lines) -> str:
def omit_deletion_hunks(patch_lines) -> str:
"""
Omit deletion hunks from the patch and return the modified patch.
Args:
- patch_lines: a list of strings representing the lines of the patch
Returns:
- A string representing the modified patch with deletion hunks omitted
"""
temp_hunk = []
added_patched = []
add_hunk = False
@ -88,20 +116,188 @@ def omit_deletion_hunks(patch_lines) -> str:
def handle_patch_deletions(patch: str, original_file_content_str: str,
new_file_content_str: str, file_name: str) -> str:
new_file_content_str: str, file_name: str, edit_type: EDIT_TYPE = EDIT_TYPE.UNKNOWN) -> str:
"""
Handle entire file or deletion patches
Handle entire file or deletion patches.
This function takes a patch, original file content, new file content, and file name as input.
It handles entire file or deletion patches and returns the modified patch with deletion hunks omitted.
Args:
patch (str): The patch to be handled.
original_file_content_str (str): The original content of the file.
new_file_content_str (str): The new content of the file.
file_name (str): The name of the file.
Returns:
str: The modified patch with deletion hunks omitted.
"""
if not new_file_content_str:
if not new_file_content_str and edit_type != EDIT_TYPE.ADDED:
# logic for handling deleted files - don't show patch, just show that the file was deleted
if settings.config.verbosity_level > 0:
logging.info(f"Processing file: {file_name}, minimizing deletion file")
if get_settings().config.verbosity_level > 0:
get_logger().info(f"Processing file: {file_name}, minimizing deletion file")
patch = None # file was deleted
else:
patch_lines = patch.splitlines()
patch_new = omit_deletion_hunks(patch_lines)
if patch != patch_new:
if settings.config.verbosity_level > 0:
logging.info(f"Processing file: {file_name}, hunks were deleted")
if get_settings().config.verbosity_level > 0:
get_logger().info(f"Processing file: {file_name}, hunks were deleted")
patch = patch_new
return patch
def convert_to_hunks_with_lines_numbers(patch: str, file) -> str:
"""
Convert a given patch string into a string with line numbers for each hunk, indicating the new and old content of
the file.
Args:
patch (str): The patch string to be converted.
file: An object containing the filename of the file being patched.
Returns:
str: A string with line numbers for each hunk, indicating the new and old content of the file.
example output:
## src/file.ts
__new hunk__
881 line1
882 line2
883 line3
887 + line4
888 + line5
889 line6
890 line7
...
__old hunk__
line1
line2
- line3
- line4
line5
line6
...
"""
patch_with_lines_str = f"\n\n## file: '{file.filename.strip()}'\n"
patch_lines = patch.splitlines()
RE_HUNK_HEADER = re.compile(
r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)")
new_content_lines = []
old_content_lines = []
match = None
start1, size1, start2, size2 = -1, -1, -1, -1
prev_header_line = []
header_line =[]
for line in patch_lines:
if 'no newline at end of file' in line.lower():
continue
if line.startswith('@@'):
header_line = line
match = RE_HUNK_HEADER.match(line)
if match and new_content_lines: # found a new hunk, split the previous lines
if new_content_lines:
if prev_header_line:
patch_with_lines_str += f'\n{prev_header_line}\n'
patch_with_lines_str = patch_with_lines_str.rstrip()+'\n__new hunk__\n'
for i, line_new in enumerate(new_content_lines):
patch_with_lines_str += f"{start2 + i} {line_new}\n"
if old_content_lines:
patch_with_lines_str = patch_with_lines_str.rstrip()+'\n__old hunk__\n'
for line_old in old_content_lines:
patch_with_lines_str += f"{line_old}\n"
new_content_lines = []
old_content_lines = []
if match:
prev_header_line = header_line
res = list(match.groups())
for i in range(len(res)):
if res[i] is None:
res[i] = 0
try:
start1, size1, start2, size2 = map(int, res[:4])
except: # '@@ -0,0 +1 @@' case
start1, size1, size2 = map(int, res[:3])
start2 = 0
elif line.startswith('+'):
new_content_lines.append(line)
elif line.startswith('-'):
old_content_lines.append(line)
else:
new_content_lines.append(line)
old_content_lines.append(line)
# finishing last hunk
if match and new_content_lines:
if new_content_lines:
patch_with_lines_str += f'\n{header_line}\n'
patch_with_lines_str = patch_with_lines_str.rstrip()+ '\n__new hunk__\n'
for i, line_new in enumerate(new_content_lines):
patch_with_lines_str += f"{start2 + i} {line_new}\n"
if old_content_lines:
patch_with_lines_str = patch_with_lines_str.rstrip() + '\n__old hunk__\n'
for line_old in old_content_lines:
patch_with_lines_str += f"{line_old}\n"
return patch_with_lines_str.rstrip()
def extract_hunk_lines_from_patch(patch: str, file_name, line_start, line_end, side) -> tuple[str, str]:
patch_with_lines_str = f"\n\n## file: '{file_name.strip()}'\n\n"
selected_lines = ""
patch_lines = patch.splitlines()
RE_HUNK_HEADER = re.compile(
r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)")
match = None
start1, size1, start2, size2 = -1, -1, -1, -1
skip_hunk = False
selected_lines_num = 0
for line in patch_lines:
if 'no newline at end of file' in line.lower():
continue
if line.startswith('@@'):
skip_hunk = False
selected_lines_num = 0
header_line = line
match = RE_HUNK_HEADER.match(line)
res = list(match.groups())
for i in range(len(res)):
if res[i] is None:
res[i] = 0
try:
start1, size1, start2, size2 = map(int, res[:4])
except: # '@@ -0,0 +1 @@' case
start1, size1, size2 = map(int, res[:3])
start2 = 0
# check if line range is in this hunk
if side.lower() == 'left':
# check if line range is in this hunk
if not (start1 <= line_start <= start1 + size1):
skip_hunk = True
continue
elif side.lower() == 'right':
if not (start2 <= line_start <= start2 + size2):
skip_hunk = True
continue
patch_with_lines_str += f'\n{header_line}\n'
elif not skip_hunk:
if side.lower() == 'right' and line_start <= start2 + selected_lines_num <= line_end:
selected_lines += line + '\n'
if side.lower() == 'left' and start1 <= selected_lines_num + start1 <= line_end:
selected_lines += line + '\n'
patch_with_lines_str += line + '\n'
if not line.startswith('-'): # currently we don't support /ask line for deleted lines
selected_lines_num += 1
return patch_with_lines_str.rstrip(), selected_lines.rstrip()

File diff suppressed because one or more lines are too long

View File

@ -1,94 +1,181 @@
from __future__ import annotations
import difflib
import logging
from typing import Any, Tuple, Union
import traceback
from typing import Callable, List, Tuple
from pr_agent.algo.git_patch_processing import extend_patch, handle_patch_deletions
from github import RateLimitExceededException
from pr_agent.algo.git_patch_processing import convert_to_hunks_with_lines_numbers, extend_patch, handle_patch_deletions
from pr_agent.algo.language_handler import sort_files_by_main_languages
from pr_agent.algo.file_filter import filter_ignored
from pr_agent.algo.token_handler import TokenHandler
from pr_agent.config_loader import settings
from pr_agent.git_providers import GithubProvider
from pr_agent.algo.utils import get_max_tokens, ModelType
from pr_agent.config_loader import get_settings
from pr_agent.git_providers.git_provider import GitProvider
from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
from pr_agent.log import get_logger
DELETED_FILES_ = "Deleted files:\n"
MORE_MODIFIED_FILES_ = "More modified files:\n"
MORE_MODIFIED_FILES_ = "Additional modified files (insufficient token budget to process):\n"
ADDED_FILES_ = "Additional added files (insufficient token budget to process):\n"
OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD = 1000
OUTPUT_BUFFER_TOKENS_HARD_THRESHOLD = 600
PATCH_EXTRA_LINES = 3
def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: str,
add_line_numbers_to_hunks: bool = False, disable_extra_lines: bool = False) -> str:
"""
Returns a string with the diff of the pull request, applying diff minimization techniques if needed.
def get_pr_diff(git_provider: Union[GithubProvider, Any], token_handler: TokenHandler) -> str:
Args:
git_provider (GitProvider): An object of the GitProvider class representing the Git provider used for the pull
request.
token_handler (TokenHandler): An object of the TokenHandler class used for handling tokens in the context of the
pull request.
model (str): The name of the model used for tokenization.
add_line_numbers_to_hunks (bool, optional): A boolean indicating whether to add line numbers to the hunks in the
diff. Defaults to False.
disable_extra_lines (bool, optional): A boolean indicating whether to disable the extension of each patch with
extra lines of context. Defaults to False.
Returns:
str: A string with the diff of the pull request, applying diff minimization techniques if needed.
"""
Returns a string with the diff of the PR.
If needed, apply diff minimization techniques to reduce the number of tokens
"""
files = list(git_provider.get_diff_files())
if disable_extra_lines:
PATCH_EXTRA_LINES = 0
else:
PATCH_EXTRA_LINES = get_settings().config.patch_extra_lines
try:
diff_files_original = git_provider.get_diff_files()
except RateLimitExceededException as e:
get_logger().error(f"Rate limit exceeded for git provider API. original message {e}")
raise
diff_files = filter_ignored(diff_files_original)
if diff_files != diff_files_original:
try:
get_logger().info(f"Filtered out {len(diff_files_original) - len(diff_files)} files")
new_names = set([a.filename for a in diff_files])
orig_names = set([a.filename for a in diff_files_original])
get_logger().info(f"Filtered out files: {orig_names - new_names}")
except Exception as e:
pass
# get pr languages
pr_languages = sort_files_by_main_languages(git_provider.get_languages(), files)
pr_languages = sort_files_by_main_languages(git_provider.get_languages(), diff_files)
if pr_languages:
try:
get_logger().info(f"PR main language: {pr_languages[0]['language']}")
except Exception as e:
pass
# generate a standard diff string, with patch extension
patches_extended, total_tokens = pr_generate_extended_diff(pr_languages, token_handler)
patches_extended, total_tokens, patches_extended_tokens = pr_generate_extended_diff(
pr_languages, token_handler, add_line_numbers_to_hunks, patch_extra_lines=PATCH_EXTRA_LINES)
# if we are under the limit, return the full diff
if total_tokens + OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD < token_handler.limit:
if total_tokens + OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD < get_max_tokens(model):
get_logger().info(f"Tokens: {total_tokens}, total tokens under limit: {get_max_tokens(model)}, "
f"returning full diff.")
return "\n".join(patches_extended)
# if we are over the limit, start pruning
patches_compressed, modified_file_names, deleted_file_names = pr_generate_compressed_diff(pr_languages,
token_handler)
get_logger().info(f"Tokens: {total_tokens}, total tokens over limit: {get_max_tokens(model)}, "
f"pruning diff.")
patches_compressed, modified_file_names, deleted_file_names, added_file_names = \
pr_generate_compressed_diff(pr_languages, token_handler, model, add_line_numbers_to_hunks)
final_diff = "\n".join(patches_compressed)
if added_file_names:
added_list_str = ADDED_FILES_ + "\n".join(added_file_names)
final_diff = final_diff + "\n\n" + added_list_str
if modified_file_names:
modified_list_str = MORE_MODIFIED_FILES_ + "\n".join(modified_file_names)
final_diff = final_diff + "\n\n" + modified_list_str
if deleted_file_names:
deleted_list_str = DELETED_FILES_ + "\n".join(deleted_file_names)
final_diff = final_diff + "\n\n" + deleted_list_str
try:
get_logger().debug(f"After pruning, added_list_str: {added_list_str}, modified_list_str: {modified_list_str}, "
f"deleted_list_str: {deleted_list_str}")
except Exception as e:
pass
return final_diff
def pr_generate_extended_diff(pr_languages: list, token_handler: TokenHandler) -> \
Tuple[list, int]:
def pr_generate_extended_diff(pr_languages: list,
token_handler: TokenHandler,
add_line_numbers_to_hunks: bool,
patch_extra_lines: int = 0) -> Tuple[list, int, list]:
"""
Generate a standard diff string, with patch extension
Generate a standard diff string with patch extension, while counting the number of tokens used and applying diff
minimization techniques if needed.
Args:
- pr_languages: A list of dictionaries representing the languages used in the pull request and their corresponding
files.
- token_handler: An object of the TokenHandler class used for handling tokens in the context of the pull request.
- add_line_numbers_to_hunks: A boolean indicating whether to add line numbers to the hunks in the diff.
"""
total_tokens = token_handler.prompt_tokens # initial tokens
patches_extended = []
patches_extended_tokens = []
for lang in pr_languages:
for file in lang['files']:
original_file_content_str = file.base_file
new_file_content_str = file.head_file
patch = file.patch
# handle the case of large patch, that initially was not loaded
patch = load_large_diff(file, new_file_content_str, original_file_content_str, patch)
if not patch:
continue
# extend each patch with extra lines of context
extended_patch = extend_patch(original_file_content_str, patch, num_lines=PATCH_EXTRA_LINES)
full_extended_patch = f"## {file.filename}\n\n{extended_patch}\n"
extended_patch = extend_patch(original_file_content_str, patch, num_lines=patch_extra_lines)
full_extended_patch = f"\n\n## {file.filename}\n\n{extended_patch}\n"
if add_line_numbers_to_hunks:
full_extended_patch = convert_to_hunks_with_lines_numbers(extended_patch, file)
patch_tokens = token_handler.count_tokens(full_extended_patch)
file.tokens = patch_tokens
total_tokens += patch_tokens
patches_extended_tokens.append(patch_tokens)
patches_extended.append(full_extended_patch)
return patches_extended, total_tokens
return patches_extended, total_tokens, patches_extended_tokens
def pr_generate_compressed_diff(top_langs: list, token_handler: TokenHandler) -> Tuple[list, list, list]:
# Apply Diff Minimization techniques to reduce the number of tokens:
# 0. Start from the largest diff patch to smaller ones
# 1. Don't use extend context lines around diff
# 2. Minimize deleted files
# 3. Minimize deleted hunks
# 4. Minimize all remaining files when you reach token limit
def pr_generate_compressed_diff(top_langs: list, token_handler: TokenHandler, model: str,
convert_hunks_to_line_numbers: bool) -> Tuple[list, list, list, list]:
"""
Generate a compressed diff string for a pull request, using diff minimization techniques to reduce the number of
tokens used.
Args:
top_langs (list): A list of dictionaries representing the languages used in the pull request and their
corresponding files.
token_handler (TokenHandler): An object of the TokenHandler class used for handling tokens in the context of the
pull request.
model (str): The model used for tokenization.
convert_hunks_to_line_numbers (bool): A boolean indicating whether to convert hunks to line numbers in the diff.
Returns:
Tuple[list, list, list]: A tuple containing the following lists:
- patches: A list of compressed diff patches for each file in the pull request.
- modified_files_list: A list of file names that were skipped due to large patch size.
- deleted_files_list: A list of file names that were deleted in the pull request.
Minimization techniques to reduce the number of tokens:
0. Start from the largest diff patch to smaller ones
1. Don't use extend context lines around diff
2. Minimize deleted files
3. Minimize deleted hunks
4. Minimize all remaining files when you reach token limit
"""
patches = []
added_files_list = []
modified_files_list = []
deleted_files_list = []
# sort each one of the languages in top_langs by the number of tokens in the diff
@ -101,57 +188,198 @@ def pr_generate_compressed_diff(top_langs: list, token_handler: TokenHandler) ->
original_file_content_str = file.base_file
new_file_content_str = file.head_file
patch = file.patch
patch = load_large_diff(file, new_file_content_str, original_file_content_str, patch)
if not patch:
continue
# removing delete-only hunks
patch = handle_patch_deletions(patch, original_file_content_str,
new_file_content_str, file.filename)
new_file_content_str, file.filename, file.edit_type)
if patch is None:
if not deleted_files_list:
total_tokens += token_handler.count_tokens(DELETED_FILES_)
deleted_files_list.append(file.filename)
total_tokens += token_handler.count_tokens(file.filename) + 1
continue
if convert_hunks_to_line_numbers:
patch = convert_to_hunks_with_lines_numbers(patch, file)
new_patch_tokens = token_handler.count_tokens(patch)
# Hard Stop, no more tokens
if total_tokens > token_handler.limit - OUTPUT_BUFFER_TOKENS_HARD_THRESHOLD:
logging.warning(f"File was fully skipped, no more tokens: {file.filename}.")
if total_tokens > get_max_tokens(model) - OUTPUT_BUFFER_TOKENS_HARD_THRESHOLD:
get_logger().warning(f"File was fully skipped, no more tokens: {file.filename}.")
continue
# If the patch is too large, just show the file name
if total_tokens + new_patch_tokens > token_handler.limit - OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD:
if total_tokens + new_patch_tokens > get_max_tokens(model) - OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD:
# Current logic is to skip the patch if it's too large
# TODO: Option for alternative logic to remove hunks from the patch to reduce the number of tokens
# until we meet the requirements
if settings.config.verbosity_level >= 2:
logging.warning(f"Patch too large, minimizing it, {file.filename}")
if not modified_files_list:
total_tokens += token_handler.count_tokens(MORE_MODIFIED_FILES_)
modified_files_list.append(file.filename)
total_tokens += token_handler.count_tokens(file.filename) + 1
if get_settings().config.verbosity_level >= 2:
get_logger().warning(f"Patch too large, minimizing it, {file.filename}")
if file.edit_type == EDIT_TYPE.ADDED:
if not added_files_list:
total_tokens += token_handler.count_tokens(ADDED_FILES_)
added_files_list.append(file.filename)
else:
if not modified_files_list:
total_tokens += token_handler.count_tokens(MORE_MODIFIED_FILES_)
modified_files_list.append(file.filename)
total_tokens += token_handler.count_tokens(file.filename) + 1
continue
if patch:
patch_final = f"## {file.filename}\n\n{patch}\n"
if not convert_hunks_to_line_numbers:
patch_final = f"\n\n## file: '{file.filename.strip()}\n\n{patch.strip()}\n'"
else:
patch_final = "\n\n" + patch.strip()
patches.append(patch_final)
total_tokens += token_handler.count_tokens(patch_final)
if settings.config.verbosity_level >= 2:
logging.info(f"Tokens: {total_tokens}, last filename: {file.filename}")
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"Tokens: {total_tokens}, last filename: {file.filename}")
return patches, modified_files_list, deleted_files_list
return patches, modified_files_list, deleted_files_list, added_files_list
def load_large_diff(file, new_file_content_str: str, original_file_content_str: str, patch: str) -> str:
if not patch: # to Do - also add condition for file extension
async def retry_with_fallback_models(f: Callable, model_type: ModelType = ModelType.REGULAR):
all_models = _get_all_models(model_type)
all_deployments = _get_all_deployments(all_models)
# try each (model, deployment_id) pair until one is successful, otherwise raise exception
for i, (model, deployment_id) in enumerate(zip(all_models, all_deployments)):
try:
diff = difflib.unified_diff(original_file_content_str.splitlines(keepends=True),
new_file_content_str.splitlines(keepends=True))
if settings.config.verbosity_level >= 2:
logging.warning(f"File was modified, but no patch was found. Manually creating patch: {file.filename}.")
patch = ''.join(diff)
except Exception:
pass
return patch
get_logger().debug(
f"Generating prediction with {model}"
f"{(' from deployment ' + deployment_id) if deployment_id else ''}"
)
get_settings().set("openai.deployment_id", deployment_id)
return await f(model)
except:
get_logger().warning(
f"Failed to generate prediction with {model}"
f"{(' from deployment ' + deployment_id) if deployment_id else ''}: "
f"{traceback.format_exc()}"
)
if i == len(all_models) - 1: # If it's the last iteration
raise # Re-raise the last exception
def _get_all_models(model_type: ModelType = ModelType.REGULAR) -> List[str]:
if model_type == ModelType.TURBO:
model = get_settings().config.model_turbo
else:
model = get_settings().config.model
fallback_models = get_settings().config.fallback_models
if not isinstance(fallback_models, list):
fallback_models = [m.strip() for m in fallback_models.split(",")]
all_models = [model] + fallback_models
return all_models
def _get_all_deployments(all_models: List[str]) -> List[str]:
deployment_id = get_settings().get("openai.deployment_id", None)
fallback_deployments = get_settings().get("openai.fallback_deployments", [])
if not isinstance(fallback_deployments, list) and fallback_deployments:
fallback_deployments = [d.strip() for d in fallback_deployments.split(",")]
if fallback_deployments:
all_deployments = [deployment_id] + fallback_deployments
if len(all_deployments) < len(all_models):
raise ValueError(f"The number of deployments ({len(all_deployments)}) "
f"is less than the number of models ({len(all_models)})")
else:
all_deployments = [deployment_id] * len(all_models)
return all_deployments
def get_pr_multi_diffs(git_provider: GitProvider,
token_handler: TokenHandler,
model: str,
max_calls: int = 5) -> List[str]:
"""
Retrieves the diff files from a Git provider, sorts them by main language, and generates patches for each file.
The patches are split into multiple groups based on the maximum number of tokens allowed for the given model.
Args:
git_provider (GitProvider): An object that provides access to Git provider APIs.
token_handler (TokenHandler): An object that handles tokens in the context of a pull request.
model (str): The name of the model.
max_calls (int, optional): The maximum number of calls to retrieve diff files. Defaults to 5.
Returns:
List[str]: A list of final diff strings, split into multiple groups based on the maximum number of tokens allowed for the given model.
Raises:
RateLimitExceededException: If the rate limit for the Git provider API is exceeded.
"""
try:
diff_files = git_provider.get_diff_files()
except RateLimitExceededException as e:
get_logger().error(f"Rate limit exceeded for git provider API. original message {e}")
raise
diff_files = filter_ignored(diff_files)
# Sort files by main language
pr_languages = sort_files_by_main_languages(git_provider.get_languages(), diff_files)
# Sort files within each language group by tokens in descending order
sorted_files = []
for lang in pr_languages:
sorted_files.extend(sorted(lang['files'], key=lambda x: x.tokens, reverse=True))
# try first a single run with standard diff string, with patch extension, and no deletions
patches_extended, total_tokens, patches_extended_tokens = pr_generate_extended_diff(
pr_languages, token_handler, add_line_numbers_to_hunks=True)
if total_tokens + OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD < get_max_tokens(model):
return ["\n".join(patches_extended)]
patches = []
final_diff_list = []
total_tokens = token_handler.prompt_tokens
call_number = 1
for file in sorted_files:
if call_number > max_calls:
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"Reached max calls ({max_calls})")
break
original_file_content_str = file.base_file
new_file_content_str = file.head_file
patch = file.patch
if not patch:
continue
# Remove delete-only hunks
patch = handle_patch_deletions(patch, original_file_content_str, new_file_content_str, file.filename, file.edit_type)
if patch is None:
continue
patch = convert_to_hunks_with_lines_numbers(patch, file)
new_patch_tokens = token_handler.count_tokens(patch)
if patch and (token_handler.prompt_tokens + new_patch_tokens) > get_max_tokens(model) - OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD:
get_logger().warning(f"Patch too large, skipping: {file.filename}")
continue
if patch and (total_tokens + new_patch_tokens > get_max_tokens(model) - OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD):
final_diff = "\n".join(patches)
final_diff_list.append(final_diff)
patches = []
total_tokens = token_handler.prompt_tokens
call_number += 1
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"Call number: {call_number}")
if patch:
patches.append(patch)
total_tokens += new_patch_tokens
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"Tokens: {total_tokens}, last filename: {file.filename}")
# Add the last chunk
if patches:
final_diff = "\n".join(patches)
final_diff_list.append(final_diff)
return final_diff_list

View File

@ -1,24 +1,69 @@
from jinja2 import Environment, StrictUndefined
from tiktoken import encoding_for_model
from tiktoken import encoding_for_model, get_encoding
from pr_agent.algo import MAX_TOKENS
from pr_agent.config_loader import settings
from pr_agent.config_loader import get_settings
def get_token_encoder():
return encoding_for_model(get_settings().config.model) if "gpt" in get_settings().config.model else get_encoding(
"cl100k_base")
class TokenHandler:
def __init__(self, pr, vars: dict, system, user):
self.encoder = encoding_for_model(settings.config.model)
self.limit = MAX_TOKENS[settings.config.model]
self.prompt_tokens = self._get_system_user_tokens(pr, self.encoder, vars, system, user)
"""
A class for handling tokens in the context of a pull request.
Attributes:
- encoder: An object of the encoding_for_model class from the tiktoken module. Used to encode strings and count the
number of tokens in them.
- limit: The maximum number of tokens allowed for the given model, as defined in the MAX_TOKENS dictionary in the
pr_agent.algo module.
- prompt_tokens: The number of tokens in the system and user strings, as calculated by the _get_system_user_tokens
method.
"""
def __init__(self, pr=None, vars: dict = {}, system="", user=""):
"""
Initializes the TokenHandler object.
Args:
- pr: The pull request object.
- vars: A dictionary of variables.
- system: The system string.
- user: The user string.
"""
self.encoder = get_token_encoder()
if pr is not None:
self.prompt_tokens = self._get_system_user_tokens(pr, self.encoder, vars, system, user)
def _get_system_user_tokens(self, pr, encoder, vars: dict, system, user):
"""
Calculates the number of tokens in the system and user strings.
Args:
- pr: The pull request object.
- encoder: An object of the encoding_for_model class from the tiktoken module.
- vars: A dictionary of variables.
- system: The system string.
- user: The user string.
Returns:
The sum of the number of tokens in the system and user strings.
"""
environment = Environment(undefined=StrictUndefined)
system_prompt = environment.from_string(system).render(vars)
user_prompt = environment.from_string(user).render(vars)
system_prompt_tokens = len(encoder.encode(system_prompt))
user_prompt_tokens = len(encoder.encode(user_prompt))
return system_prompt_tokens + user_prompt_tokens
def count_tokens(self, patch: str) -> int:
"""
Counts the number of tokens in a given patch string.
Args:
- patch: The patch string.
Returns:
The number of tokens in the patch string.
"""
return len(self.encoder.encode(patch, disallowed_special=()))

23
pr_agent/algo/types.py Normal file
View File

@ -0,0 +1,23 @@
from dataclasses import dataclass
from enum import Enum
class EDIT_TYPE(Enum):
ADDED = 1
DELETED = 2
MODIFIED = 3
RENAMED = 4
UNKNOWN = 5
@dataclass
class FilePatchInfo:
base_file: str
head_file: str
patch: str
filename: str
tokens: int = -1
edit_type: EDIT_TYPE = EDIT_TYPE.UNKNOWN
old_filename: str = None
num_plus_lines: int = -1
num_minus_lines: int = -1

View File

@ -1,88 +1,606 @@
from __future__ import annotations
import difflib
import json
import logging
import re
import textwrap
from datetime import datetime
from enum import Enum
from typing import Any, List, Tuple
import yaml
from starlette_context import context
from pr_agent.algo import MAX_TOKENS
from pr_agent.algo.token_handler import get_token_encoder
from pr_agent.config_loader import get_settings, global_settings
from pr_agent.algo.types import FilePatchInfo
from pr_agent.log import get_logger
class ModelType(str, Enum):
REGULAR = "regular"
TURBO = "turbo"
def get_setting(key: str) -> Any:
try:
key = key.upper()
return context.get("settings", global_settings).get(key, global_settings.get(key, None))
except Exception:
return global_settings.get(key, None)
def convert_to_markdown(output_data: dict) -> str:
markdown_text = ""
def emphasize_header(text: str) -> str:
try:
# Finding the position of the first occurrence of ": "
colon_position = text.find(": ")
# Splitting the string and wrapping the first part in <strong> tags
if colon_position != -1:
# Everything before the colon (inclusive) is wrapped in <strong> tags
transformed_string = "<strong>" + text[:colon_position + 1] + "</strong>" + text[colon_position + 1:]
else:
# If there's no ": ", return the original string
transformed_string = text
return transformed_string
except Exception as e:
get_logger().exception(f"Failed to emphasize header: {e}")
return text
def convert_to_markdown(output_data: dict, gfm_supported: bool=True) -> str:
"""
Convert a dictionary of data into markdown format.
Args:
output_data (dict): A dictionary containing data to be converted to markdown format.
Returns:
str: The markdown formatted text generated from the input dictionary.
"""
emojis = {
"Main theme": "🎯",
"Description and title": "🔍",
"Type of PR": "📌",
"Relevant tests added": "🧪",
"Unrelated changes": "⚠️",
"Possible issues": "🔍",
"Score": "🏅",
"Relevant tests": "🧪",
"Focused PR": "",
"Security concerns": "🔒",
"General PR suggestions": "💡",
"Code suggestions": "🤖"
"Insights from user's answers": "📝",
"Code feedback": "🤖",
"Estimated effort to review [1-5]": "⏱️",
}
for key, value in output_data.items():
if not value:
continue
if isinstance(value, dict):
markdown_text += f"## {key}\n\n"
markdown_text += convert_to_markdown(value)
elif isinstance(value, list):
if key.lower() == 'code suggestions':
markdown_text += "\n" # just looks nicer with additional line breaks
emoji = emojis.get(key, "") # Use a dash if no emoji is found for the key
markdown_text += f"- {emoji} **{key}:**\n\n"
for item in value:
if isinstance(item, dict) and key.lower() == 'code suggestions':
markdown_text += parse_code_suggestion(item)
elif item:
markdown_text += f" - {item}\n"
elif value != 'n/a':
emoji = emojis.get(key, "") # Use a dash if no emoji is found for the key
markdown_text += f"- {emoji} **{key}:** {value}\n"
return markdown_text
def parse_code_suggestion(code_suggestions: dict) -> str:
markdown_text = ""
for sub_key, sub_value in code_suggestions.items():
if isinstance(sub_value, dict): # "code example"
markdown_text += f" - **{sub_key}:**\n"
for code_key, code_value in sub_value.items(): # 'before' and 'after' code
code_str = f"```\n{code_value}\n```"
code_str_indented = textwrap.indent(code_str, ' ')
markdown_text += f" - **{code_key}:**\n{code_str_indented}\n"
else:
if "suggestion number" in sub_key.lower():
# markdown_text += f"- **suggestion {sub_value}:**\n" # prettier formatting
pass
elif "relevant file" in sub_key.lower():
markdown_text += f"\n - **{sub_key}:** {sub_value}\n"
else:
markdown_text += f" **{sub_key}:** {sub_value}\n"
markdown_text += f"## PR Review\n\n"
if gfm_supported:
markdown_text += "<table>\n<tr>\n"
# markdown_text += """<td> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Feedback&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td> <td></td></tr>"""
if not output_data or not output_data.get('review', {}):
return ""
for key, value in output_data['review'].items():
if value is None or value == '' or value == {} or value == []:
continue
key_nice = key.replace('_', ' ').capitalize()
emoji = emojis.get(key_nice, "")
if gfm_supported:
if 'Estimated effort to review' in key_nice:
key_nice = 'Estimated&nbsp;effort&nbsp;to&nbsp;review [1-5]'
if 'security concerns' in key_nice.lower():
value = emphasize_header(value.strip())
markdown_text += f"<tr><td> {emoji}&nbsp;<strong>{key_nice}</strong></td><td>\n\n{value}\n\n</td></tr>\n"
elif 'possible issues' in key_nice.lower():
value = value.strip()
issues = value.split('\n- ')
number_of_issues = len(issues)
if number_of_issues > 1:
markdown_text += f"<tr><td rowspan={number_of_issues}> {emoji}&nbsp;<strong>{key_nice}</strong></td>\n"
for i, issue in enumerate(issues):
issue = issue.strip('-').strip()
issue = emphasize_header(issue.strip())
if i == 0:
markdown_text += f"<td>\n\n{issue}</td></tr>\n"
else:
markdown_text += f"<tr>\n<td>\n\n{issue}</td></tr>\n"
else:
value = emphasize_header(value.strip('-').strip())
markdown_text += f"<tr><td> {emoji}&nbsp;<strong>{key_nice}</strong></td><td>\n\n{value}\n\n</td></tr>\n"
else:
markdown_text += f"<tr><td> {emoji}&nbsp;<strong>{key_nice}</strong></td><td>\n\n{value}\n\n</td></tr>\n"
else:
if len(value.split()) > 1:
markdown_text += f"{emoji} **{key_nice}:**\n\n {value}\n\n"
else:
markdown_text += f"{emoji} **{key_nice}:** {value}\n\n"
if gfm_supported:
markdown_text += "</table>\n"
if 'code_feedback' in output_data:
if gfm_supported:
markdown_text += f"\n\n"
markdown_text += f"<details><summary> <strong>Code feedback:</strong></summary>\n\n"
markdown_text += "<hr>"
else:
markdown_text += f"\n\n** Code feedback:**\n\n"
for i, value in enumerate(output_data['code_feedback']):
if value is None or value == '' or value == {} or value == []:
continue
markdown_text += parse_code_suggestion(value, i, gfm_supported)+"\n\n"
if markdown_text.endswith('<hr>'):
markdown_text = markdown_text[:-4]
if gfm_supported:
markdown_text += f"</details>"
#print(markdown_text)
markdown_text += "\n"
return markdown_text
def try_fix_json(review, max_iter=10):
# Try to fix JSON if it is broken/incomplete: parse until the last valid code suggestion
def parse_code_suggestion(code_suggestion: dict, i: int = 0, gfm_supported: bool = True) -> str:
"""
Convert a dictionary of data into markdown format.
Args:
code_suggestion (dict): A dictionary containing data to be converted to markdown format.
Returns:
str: A string containing the markdown formatted text generated from the input dictionary.
"""
markdown_text = ""
if gfm_supported and 'relevant_line' in code_suggestion:
markdown_text += '<table>'
for sub_key, sub_value in code_suggestion.items():
try:
if sub_key.lower() == 'relevant_file':
relevant_file = sub_value.strip('`').strip('"').strip("'")
markdown_text += f"<tr><td>relevant file</td><td>{relevant_file}</td></tr>"
# continue
elif sub_key.lower() == 'suggestion':
markdown_text += (f"<tr><td>{sub_key} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>"
f"<td>\n\n<strong>\n\n{sub_value.strip()}\n\n</strong>\n</td></tr>")
elif sub_key.lower() == 'relevant_line':
markdown_text += f"<tr><td>relevant line</td>"
sub_value_list = sub_value.split('](')
relevant_line = sub_value_list[0].lstrip('`').lstrip('[')
if len(sub_value_list) > 1:
link = sub_value_list[1].rstrip(')').strip('`')
markdown_text += f"<td><a href='{link}'>{relevant_line}</a></td>"
else:
markdown_text += f"<td>{relevant_line}</td>"
markdown_text += "</tr>"
except Exception as e:
get_logger().exception(f"Failed to parse code suggestion: {e}")
pass
markdown_text += '</table>'
markdown_text += "<hr>"
else:
for sub_key, sub_value in code_suggestion.items():
if isinstance(sub_key, str):
sub_key = sub_key.rstrip()
if isinstance(sub_value,str):
sub_value = sub_value.rstrip()
if isinstance(sub_value, dict): # "code example"
markdown_text += f" - **{sub_key}:**\n"
for code_key, code_value in sub_value.items(): # 'before' and 'after' code
code_str = f"```\n{code_value}\n```"
code_str_indented = textwrap.indent(code_str, ' ')
markdown_text += f" - **{code_key}:**\n{code_str_indented}\n"
else:
if "relevant_file" in sub_key.lower():
markdown_text += f"\n - **{sub_key}:** {sub_value} \n"
else:
markdown_text += f" **{sub_key}:** {sub_value} \n"
if "relevant_line" not in sub_key.lower(): # nicer presentation
# markdown_text = markdown_text.rstrip('\n') + "\\\n" # works for gitlab
markdown_text = markdown_text.rstrip('\n') + " \n" # works for gitlab and bitbucker
markdown_text += "\n"
return markdown_text
def try_fix_json(review, max_iter=10, code_suggestions=False):
"""
Fix broken or incomplete JSON messages and return the parsed JSON data.
Args:
- review: A string containing the JSON message to be fixed.
- max_iter: An integer representing the maximum number of iterations to try and fix the JSON message.
- code_suggestions: A boolean indicating whether to try and fix JSON messages with code feedback.
Returns:
- data: A dictionary containing the parsed JSON data.
The function attempts to fix broken or incomplete JSON messages by parsing until the last valid code suggestion.
If the JSON message ends with a closing bracket, the function calls the fix_json_escape_char function to fix the
message.
If code_suggestions is True and the JSON message contains code feedback, the function tries to fix the JSON
message by parsing until the last valid code suggestion.
The function uses regular expressions to find the last occurrence of "}," with any number of whitespaces or
newlines.
It tries to parse the JSON message with the closing bracket and checks if it is valid.
If the JSON message is valid, the parsed JSON data is returned.
If the JSON message is not valid, the last code suggestion is removed and the process is repeated until a valid JSON
message is obtained or the maximum number of iterations is reached.
If a valid JSON message is not obtained, an error is logged and an empty dictionary is returned.
"""
if review.endswith("}"):
return fix_json_escape_char(review)
data = {}
if review.rfind("'Code suggestions': [") > 0 or review.rfind('"Code suggestions": [') > 0:
if code_suggestions:
closing_bracket = "]}"
else:
closing_bracket = "]}}"
if (review.rfind("'Code feedback': [") > 0 or review.rfind('"Code feedback": [') > 0) or \
(review.rfind("'Code suggestions': [") > 0 or review.rfind('"Code suggestions": [') > 0) :
last_code_suggestion_ind = [m.end() for m in re.finditer(r"\}\s*,", review)][-1] - 1
valid_json = False
iter_count = 0
while last_code_suggestion_ind > 0 and not valid_json and iter_count < max_iter:
try:
data = json.loads(review[:last_code_suggestion_ind] + "]}}")
data = json.loads(review[:last_code_suggestion_ind] + closing_bracket)
valid_json = True
review = review[:last_code_suggestion_ind].strip() + "]}}"
review = review[:last_code_suggestion_ind].strip() + closing_bracket
except json.decoder.JSONDecodeError:
review = review[:last_code_suggestion_ind]
# Use regular expression to find the last occurrence of "}," with any number of whitespaces or newlines
last_code_suggestion_ind = [m.end() for m in re.finditer(r"\}\s*,", review)][-1] - 1
iter_count += 1
if not valid_json:
logging.error("Unable to decode JSON response from AI")
get_logger().error("Unable to decode JSON response from AI")
data = {}
return data
def fix_json_escape_char(json_message=None):
"""
Fix broken or incomplete JSON messages and return the parsed JSON data.
Args:
json_message (str): A string containing the JSON message to be fixed.
Returns:
dict: A dictionary containing the parsed JSON data.
Raises:
None
"""
try:
result = json.loads(json_message)
except Exception as e:
# Find the offending character index:
idx_to_replace = int(str(e).split(' ')[-1].replace(')', ''))
# Remove the offending character:
json_message = list(json_message)
json_message[idx_to_replace] = ' '
new_message = ''.join(json_message)
return fix_json_escape_char(json_message=new_message)
return result
def convert_str_to_datetime(date_str):
"""
Convert a string representation of a date and time into a datetime object.
Args:
date_str (str): A string representation of a date and time in the format '%a, %d %b %Y %H:%M:%S %Z'
Returns:
datetime: A datetime object representing the input date and time.
Example:
>>> convert_str_to_datetime('Mon, 01 Jan 2022 12:00:00 UTC')
datetime.datetime(2022, 1, 1, 12, 0, 0)
"""
datetime_format = '%a, %d %b %Y %H:%M:%S %Z'
return datetime.strptime(date_str, datetime_format)
def load_large_diff(filename, new_file_content_str: str, original_file_content_str: str) -> str:
"""
Generate a patch for a modified file by comparing the original content of the file with the new content provided as
input.
Args:
new_file_content_str: The new content of the file as a string.
original_file_content_str: The original content of the file as a string.
Returns:
The generated or provided patch string.
Raises:
None.
"""
patch = ""
try:
diff = difflib.unified_diff(original_file_content_str.splitlines(keepends=True),
new_file_content_str.splitlines(keepends=True))
if get_settings().config.verbosity_level >= 2:
get_logger().warning(f"File was modified, but no patch was found. Manually creating patch: {filename}.")
patch = ''.join(diff)
except Exception:
pass
return patch
def update_settings_from_args(args: List[str]) -> List[str]:
"""
Update the settings of the Dynaconf object based on the arguments passed to the function.
Args:
args: A list of arguments passed to the function.
Example args: ['--pr_code_suggestions.extra_instructions="be funny',
'--pr_code_suggestions.num_code_suggestions=3']
Returns:
None
Raises:
ValueError: If the argument is not in the correct format.
"""
other_args = []
if args:
for arg in args:
arg = arg.strip()
if arg.startswith('--'):
arg = arg.strip('-').strip()
vals = arg.split('=', 1)
if len(vals) != 2:
if len(vals) > 2: # --extended is a valid argument
get_logger().error(f'Invalid argument format: {arg}')
other_args.append(arg)
continue
key, value = _fix_key_value(*vals)
get_settings().set(key, value)
get_logger().info(f'Updated setting {key} to: "{value}"')
else:
other_args.append(arg)
return other_args
def _fix_key_value(key: str, value: str):
key = key.strip().upper()
value = value.strip()
try:
value = yaml.safe_load(value)
except Exception as e:
get_logger().debug(f"Failed to parse YAML for config override {key}={value}", exc_info=e)
return key, value
def load_yaml(response_text: str, keys_fix_yaml: List[str] = []) -> dict:
response_text = response_text.removeprefix('```yaml').rstrip('`')
try:
data = yaml.safe_load(response_text)
except Exception as e:
get_logger().error(f"Failed to parse AI prediction: {e}")
data = try_fix_yaml(response_text, keys_fix_yaml=keys_fix_yaml)
return data
def try_fix_yaml(response_text: str, keys_fix_yaml: List[str] = []) -> dict:
response_text_lines = response_text.split('\n')
keys = ['relevant line:', 'suggestion content:', 'relevant file:', 'existing code:', 'improved code:']
keys = keys + 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)):
for key in keys:
if key in response_text_lines_copy[i] and not '|-' in response_text_lines_copy[i]:
response_text_lines_copy[i] = response_text_lines_copy[i].replace(f'{key}',
f'{key} |-\n ')
try:
data = yaml.safe_load('\n'.join(response_text_lines_copy))
get_logger().info(f"Successfully parsed AI prediction after adding |-\n")
return data
except:
get_logger().info(f"Failed to parse AI prediction after adding |-\n")
# second fallback - try to extract only range from first ```yaml to ````
snippet_pattern = r'```(yaml)?[\s\S]*?```'
snippet = re.search(snippet_pattern, '\n'.join(response_text_lines_copy))
if snippet:
snippet_text = snippet.group()
try:
data = yaml.safe_load(snippet_text.removeprefix('```yaml').rstrip('`'))
get_logger().info(f"Successfully parsed AI prediction after extracting yaml snippet")
return data
except:
pass
# third fallback - try to remove leading and trailing curly brackets
response_text_copy = response_text.strip().rstrip().removeprefix('{').removesuffix('}').rstrip(':\n')
try:
data = yaml.safe_load(response_text_copy)
get_logger().info(f"Successfully parsed AI prediction after removing curly brackets")
return data
except:
pass
# fourth fallback - try to remove last lines
data = {}
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:
return
labels = get_settings().custom_labels
if not labels:
# set default labels
labels = ['Bug fix', 'Tests', 'Bug fix with tests', 'Enhancement', 'Documentation', 'Other']
labels_list = "\n - ".join(labels) if labels else ""
labels_list = f" - {labels_list}" if labels_list else ""
variables["custom_labels"] = labels_list
return
# Set custom labels
variables["custom_labels_class"] = "class Label(str, Enum):"
counter = 0
labels_minimal_to_labels_dict = {}
for k, v in labels.items():
description = "'" + v['description'].strip('\n').replace('\n', '\\n') + "'"
# variables["custom_labels_class"] += f"\n {k.lower().replace(' ', '_')} = '{k}' # {description}"
variables["custom_labels_class"] += f"\n {k.lower().replace(' ', '_')} = {description}"
labels_minimal_to_labels_dict[k.lower().replace(' ', '_')] = k
counter += 1
variables["labels_minimal_to_labels_dict"] = labels_minimal_to_labels_dict
def get_user_labels(current_labels: List[str] = None):
"""
Only keep labels that has been added by the user
"""
try:
if current_labels is None:
current_labels = []
user_labels = []
for label in current_labels:
if label.lower() in ['bug fix', 'tests', 'enhancement', 'documentation', 'other']:
continue
if get_settings().config.enable_custom_labels:
if label in get_settings().custom_labels:
continue
user_labels.append(label)
if user_labels:
get_logger().debug(f"Keeping user labels: {user_labels}")
except Exception as e:
get_logger().exception(f"Failed to get user labels: {e}")
return current_labels
return user_labels
def get_max_tokens(model):
settings = get_settings()
if model in MAX_TOKENS:
max_tokens_model = MAX_TOKENS[model]
else:
raise Exception(f"MAX_TOKENS must be set for model {model} in ./pr_agent/algo/__init__.py")
if settings.config.max_model_tokens:
max_tokens_model = min(settings.config.max_model_tokens, max_tokens_model)
# get_logger().debug(f"limiting max tokens to {max_tokens_model}")
return max_tokens_model
def clip_tokens(text: str, max_tokens: int, add_three_dots=True) -> str:
"""
Clip the number of tokens in a string to a maximum number of tokens.
Args:
text (str): The string to clip.
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
Returns:
str: The clipped string.
"""
if not text:
return text
try:
encoder = get_token_encoder()
num_input_tokens = len(encoder.encode(text))
if num_input_tokens <= max_tokens:
return text
num_chars = len(text)
chars_per_token = num_chars / num_input_tokens
num_output_chars = int(chars_per_token * max_tokens)
clipped_text = text[:num_output_chars]
if add_three_dots:
clipped_text += "...(truncated)"
return clipped_text
except Exception as e:
get_logger().warning(f"Failed to clip tokens: {e}")
return text
def replace_code_tags(text):
"""
Replace odd instances of ` with <code> and even instances of ` with </code>
"""
parts = text.split('`')
for i in range(1, len(parts), 2):
parts[i] = '<code>' + parts[i] + '</code>'
return ''.join(parts)
def find_line_number_of_relevant_line_in_file(diff_files: List[FilePatchInfo],
relevant_file: str,
relevant_line_in_file: str,
absolute_position: int = None) -> Tuple[int, int]:
position = -1
if absolute_position is None:
absolute_position = -1
re_hunk_header = re.compile(
r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)")
for file in diff_files:
if file.filename and (file.filename.strip() == relevant_file):
patch = file.patch
patch_lines = patch.splitlines()
delta = 0
start1, size1, start2, size2 = 0, 0, 0, 0
if absolute_position != -1: # matching absolute to relative
for i, line in enumerate(patch_lines):
# new hunk
if line.startswith('@@'):
delta = 0
match = re_hunk_header.match(line)
start1, size1, start2, size2 = map(int, match.groups()[:4])
elif not line.startswith('-'):
delta += 1
#
absolute_position_curr = start2 + delta - 1
if absolute_position_curr == absolute_position:
position = i
break
else:
# try to find the line in the patch using difflib, with some margin of error
matches_difflib: list[str | Any] = difflib.get_close_matches(relevant_line_in_file,
patch_lines, n=3, cutoff=0.93)
if len(matches_difflib) == 1 and matches_difflib[0].startswith('+'):
relevant_line_in_file = matches_difflib[0]
for i, line in enumerate(patch_lines):
if line.startswith('@@'):
delta = 0
match = re_hunk_header.match(line)
start1, size1, start2, size2 = map(int, match.groups()[:4])
elif not line.startswith('-'):
delta += 1
if relevant_line_in_file in line and line[0] != '-':
position = i
absolute_position = start2 + delta - 1
break
if position == -1 and relevant_line_in_file[0] == '+':
no_plus_line = relevant_line_in_file[1:].lstrip()
for i, line in enumerate(patch_lines):
if line.startswith('@@'):
delta = 0
match = re_hunk_header.match(line)
start1, size1, start2, size2 = map(int, match.groups()[:4])
elif not line.startswith('-'):
delta += 1
if no_plus_line in line and line[0] != '-':
# The model might add a '+' to the beginning of the relevant_line_in_file even if originally
# it's a context line
position = i
absolute_position = start2 + delta - 1
break
return position, absolute_position

View File

@ -1,26 +1,68 @@
import argparse
import asyncio
import logging
import os
from pr_agent.tools.pr_questions import PRQuestions
from pr_agent.tools.pr_reviewer import PRReviewer
from pr_agent.agent.pr_agent import PRAgent, commands
from pr_agent.config_loader import get_settings
from pr_agent.log import setup_logger
log_level = os.environ.get("LOG_LEVEL", "INFO")
setup_logger(log_level)
def run():
parser = argparse.ArgumentParser(description='AI based pull request analyzer')
parser.add_argument('--pr_url', type=str, help='The URL of the PR to review', required=True)
parser.add_argument('--question', type=str, help='Optional question to ask', required=False)
args = parser.parse_args()
logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO"))
if args.question:
print(f"Question: {args.question} about PR {args.pr_url}")
reviewer = PRQuestions(args.pr_url, args.question)
asyncio.run(reviewer.answer())
def run(inargs=None):
parser = argparse.ArgumentParser(description='AI based pull request analyzer', usage=
"""\
Usage: cli.py --pr-url=<URL on supported git hosting service> <command> [<args>].
For example:
- cli.py --pr_url=... review
- cli.py --pr_url=... describe
- cli.py --pr_url=... improve
- cli.py --pr_url=... ask "write me a poem about this PR"
- cli.py --pr_url=... reflect
- cli.py --issue_url=... similar_issue
Supported commands:
- review / review_pr - Add a review that includes a summary of the PR and specific suggestions for improvement.
- ask / ask_question [question] - Ask a question about the PR.
- describe / describe_pr - Modify the PR title and description based on the PR's contents.
- improve / improve_code - Suggest improvements to the code in the PR as pull request comments ready to commit.
Extended mode ('improve --extended') employs several calls, and provides a more thorough feedback
- reflect - Ask the PR author questions about the PR.
- update_changelog - Update the changelog based on the PR's contents.
- add_docs
- generate_labels
Configuration:
To edit any configuration parameter from 'configuration.toml', just add -config_path=<value>.
For example: 'python cli.py --pr_url=... review --pr_reviewer.extra_instructions="focus on the file: ..."'
""")
parser.add_argument('--pr_url', type=str, help='The URL of the PR to review', default=None)
parser.add_argument('--issue_url', type=str, help='The URL of the Issue to review', default=None)
parser.add_argument('command', type=str, help='The', choices=commands, default='review')
parser.add_argument('rest', nargs=argparse.REMAINDER, default=[])
args = parser.parse_args(inargs)
if not args.pr_url and not args.issue_url:
parser.print_help()
return
command = args.command.lower()
get_settings().set("CONFIG.CLI_MODE", True)
if args.issue_url:
result = asyncio.run(PRAgent().handle_request(args.issue_url, [command] + args.rest))
else:
print(f"Reviewing PR: {args.pr_url}")
reviewer = PRReviewer(args.pr_url, cli_mode=True)
asyncio.run(reviewer.review())
result = asyncio.run(PRAgent().handle_request(args.pr_url, [command] + args.rest))
if not result:
parser.print_help()
if __name__ == '__main__':

View File

@ -1,16 +1,70 @@
from os.path import abspath, dirname, join
from pathlib import Path
from typing import Optional
from dynaconf import Dynaconf
from starlette_context import context
PR_AGENT_TOML_KEY = 'pr-agent'
current_dir = dirname(abspath(__file__))
settings = Dynaconf(
global_settings = Dynaconf(
envvar_prefix=False,
merge_enabled=True,
settings_files=[join(current_dir, f) for f in [
"settings/.secrets.toml",
"settings/configuration.toml",
"settings/pr_reviewer_prompts.toml",
"settings/pr_questions_prompts.toml",
"settings_prod/.secrets.toml"
]]
"settings/.secrets.toml",
"settings/configuration.toml",
"settings/ignore.toml",
"settings/language_extensions.toml",
"settings/pr_reviewer_prompts.toml",
"settings/pr_questions_prompts.toml",
"settings/pr_line_questions_prompts.toml",
"settings/pr_description_prompts.toml",
"settings/pr_code_suggestions_prompts.toml",
"settings/pr_sort_code_suggestions_prompts.toml",
"settings/pr_information_from_user_prompts.toml",
"settings/pr_update_changelog_prompts.toml",
"settings/pr_custom_labels.toml",
"settings/pr_add_docs.toml",
"settings_prod/.secrets.toml",
"settings/custom_labels.toml"
]]
)
def get_settings():
try:
return context["settings"]
except Exception:
return global_settings
# Add local configuration from pyproject.toml of the project being reviewed
def _find_repository_root() -> Path:
"""
Identify project root directory by recursively searching for the .git directory in the parent directories.
"""
cwd = Path.cwd().resolve()
no_way_up = False
while not no_way_up:
no_way_up = cwd == cwd.parent
if (cwd / ".git").is_dir():
return cwd
cwd = cwd.parent
return None
def _find_pyproject() -> Optional[Path]:
"""
Search for file pyproject.toml in the repository root.
"""
repo_root = _find_repository_root()
if repo_root:
pyproject = _find_repository_root() / "pyproject.toml"
return pyproject if pyproject.is_file() else None
return None
pyproject_path = _find_pyproject()
if pyproject_path is not None:
get_settings().load_file(pyproject_path, env=f'tool.{PR_AGENT_TOML_KEY}')

View File

@ -1,15 +1,28 @@
from pr_agent.config_loader import settings
from pr_agent.config_loader import get_settings
from pr_agent.git_providers.bitbucket_provider import BitbucketProvider
from pr_agent.git_providers.bitbucket_server_provider import BitbucketServerProvider
from pr_agent.git_providers.codecommit_provider import CodeCommitProvider
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.azuredevops_provider import AzureDevopsProvider
from pr_agent.git_providers.gerrit_provider import GerritProvider
_GIT_PROVIDERS = {
'github': GithubProvider,
'gitlab': GitLabProvider,
'bitbucket': BitbucketProvider,
'bitbucket_server': BitbucketServerProvider,
'azure': AzureDevopsProvider,
'codecommit': CodeCommitProvider,
'local' : LocalGitProvider,
'gerrit': GerritProvider,
}
def get_git_provider():
try:
provider_id = settings.config.git_provider
provider_id = get_settings().config.git_provider
except AttributeError as e:
raise ValueError("git_provider is a required attribute in the configuration file") from e
if provider_id not in _GIT_PROVIDERS:

View File

@ -0,0 +1,566 @@
import os
from typing import Optional, Tuple
from urllib.parse import urlparse
from ..log import get_logger
from ..algo.language_handler import is_valid_file
from ..algo.utils import clip_tokens, find_line_number_of_relevant_line_in_file, load_large_diff
from ..config_loader import get_settings
from .git_provider import GitProvider
from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
AZURE_DEVOPS_AVAILABLE = True
ADO_APP_CLIENT_DEFAULT_ID = "499b84ac-1321-427f-aa17-267ca6975798/.default"
MAX_PR_DESCRIPTION_AZURE_LENGTH = 4000-1
try:
# noinspection PyUnresolvedReferences
from msrest.authentication import BasicAuthentication
# noinspection PyUnresolvedReferences
from azure.devops.connection import Connection
# noinspection PyUnresolvedReferences
from azure.identity import DefaultAzureCredential
# noinspection PyUnresolvedReferences
from azure.devops.v7_1.git.models import (
Comment,
CommentThread,
GitVersionDescriptor,
GitPullRequest,
)
except ImportError:
AZURE_DEVOPS_AVAILABLE = False
class AzureDevopsProvider(GitProvider):
def __init__(
self, pr_url: Optional[str] = None, incremental: Optional[bool] = False
):
if not AZURE_DEVOPS_AVAILABLE:
raise ImportError(
"Azure DevOps provider is not available. Please install the required dependencies."
)
self.azure_devops_client = self._get_azure_devops_client()
self.diff_files = None
self.workspace_slug = None
self.repo_slug = None
self.repo = None
self.pr_num = None
self.pr = None
self.temp_comments = []
self.incremental = incremental
if pr_url:
self.set_pr(pr_url)
def publish_code_suggestions(self, code_suggestions: list) -> bool:
"""
Publishes code suggestions as comments on the PR.
"""
post_parameters_list = []
for suggestion in code_suggestions:
body = suggestion['body']
relevant_file = suggestion['relevant_file']
relevant_lines_start = suggestion['relevant_lines_start']
relevant_lines_end = suggestion['relevant_lines_end']
if not relevant_lines_start or relevant_lines_start == -1:
if get_settings().config.verbosity_level >= 2:
get_logger().exception(
f"Failed to publish code suggestion, relevant_lines_start is {relevant_lines_start}")
continue
if relevant_lines_end < relevant_lines_start:
if get_settings().config.verbosity_level >= 2:
get_logger().exception(f"Failed to publish code suggestion, "
f"relevant_lines_end is {relevant_lines_end} and "
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)
try:
for post_parameters in post_parameters_list:
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,
repository_id=self.repo_slug,
pull_request_id=self.pr_num
)
if get_settings().config.verbosity_level >= 2:
get_logger().info(
f"Published code suggestion on {self.pr_num} at {post_parameters['path']}"
)
return True
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to publish code suggestion, error: {e}")
return False
def get_pr_description_full(self) -> str:
return self.pr.description
def edit_comment(self, 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"],
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):
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"],
project=self.workspace_slug,
)
except Exception as e:
get_logger().exception(f"Failed to remove comment, error: {e}")
def publish_labels(self, pr_types):
try:
for pr_type in pr_types:
self.azure_devops_client.create_pull_request_label(
label={"name": pr_type},
project=self.workspace_slug,
repository_id=self.repo_slug,
pull_request_id=self.pr_num,
)
except Exception as e:
get_logger().exception(f"Failed to publish labels, error: {e}")
def get_pr_labels(self):
try:
labels = self.azure_devops_client.get_pull_request_labels(
project=self.workspace_slug,
repository_id=self.repo_slug,
pull_request_id=self.pr_num,
)
return [label.name for label in labels]
except Exception as e:
get_logger().exception(f"Failed to get labels, error: {e}")
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):
self.workspace_slug, self.repo_slug, self.pr_num = self._parse_pr_url(pr_url)
self.pr = self._get_pr()
def get_repo_settings(self):
try:
contents = self.azure_devops_client.get_item_content(
repository_id=self.repo_slug,
project=self.workspace_slug,
download=False,
include_content_metadata=False,
include_content=True,
path=".pr_agent.toml",
)
return list(contents)[0]
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to get repo settings, error: {e}")
return ""
def get_files(self):
files = []
for i in self.azure_devops_client.get_pull_request_commits(
project=self.workspace_slug,
repository_id=self.repo_slug,
pull_request_id=self.pr_num,
):
changes_obj = self.azure_devops_client.get_changes(
project=self.workspace_slug,
repository_id=self.repo_slug,
commit_id=i.commit_id,
)
for c in changes_obj.changes:
files.append(c["item"]["path"])
return list(set(files))
def get_diff_files(self) -> list[FilePatchInfo]:
try:
if self.diff_files:
return self.diff_files
base_sha = self.pr.last_merge_target_commit
head_sha = self.pr.last_merge_source_commit
commits = self.azure_devops_client.get_pull_request_commits(
project=self.workspace_slug,
repository_id=self.repo_slug,
pull_request_id=self.pr_num,
)
diff_files = []
diffs = []
diff_types = {}
for c in commits:
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"]
diffs = list(set(diffs))
for file in diffs:
if not is_valid_file(file):
continue
version = GitVersionDescriptor(
version=head_sha.commit_id, version_type="commit"
)
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
except Exception as error:
get_logger().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":
edit_type = EDIT_TYPE.ADDED
elif diff_types[file] == "delete":
edit_type = EDIT_TYPE.DELETED
elif diff_types[file] == "rename":
edit_type = EDIT_TYPE.RENAMED
version = GitVersionDescriptor(
version=base_sha.commit_id, version_type="commit"
)
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
except Exception as error:
get_logger().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_files.append(
FilePatchInfo(
original_file_content_str,
new_file_content_str,
patch=patch,
filename=file,
edit_type=edit_type,
)
)
self.diff_files = diff_files
return diff_files
except Exception as e:
print(f"Error: {str(e)}")
return []
def publish_comment(self, pr_comment: str, is_temporary: bool = False, thread_context=None):
comment = Comment(content=pr_comment)
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,
repository_id=self.repo_slug,
pull_request_id=self.pr_num,
)
response = {"thread_id": thread_response.id, "comment_id": thread_response.comments[0].id}
if is_temporary:
self.temp_comments.append(response)
return response
def publish_description(self, pr_title: str, pr_body: str):
if len(pr_body) > MAX_PR_DESCRIPTION_AZURE_LENGTH:
usage_guide_text='<details> <summary><strong>✨ Describe tool usage guide:</strong></summary><hr>'
ind = pr_body.find(usage_guide_text)
if ind != -1:
pr_body = pr_body[:ind]
if len(pr_body) > MAX_PR_DESCRIPTION_AZURE_LENGTH:
changes_walkthrough_text = '## **Changes walkthrough**'
ind = pr_body.find(changes_walkthrough_text)
if ind != -1:
pr_body = pr_body[:ind]
if len(pr_body) > MAX_PR_DESCRIPTION_AZURE_LENGTH:
trunction_message = " ... (description truncated due to length limit)"
pr_body = pr_body[:MAX_PR_DESCRIPTION_AZURE_LENGTH - len(trunction_message)] + trunction_message
get_logger().warning("PR description was truncated due to length limit")
try:
updated_pr = GitPullRequest()
updated_pr.title = pr_title
updated_pr.description = pr_body
self.azure_devops_client.update_pull_request(
project=self.workspace_slug,
repository_id=self.repo_slug,
pull_request_id=self.pr_num,
git_pull_request_to_update=updated_pr,
)
except Exception as e:
get_logger().exception(
f"Could not update pull request {self.pr_num} description: {e}"
)
def remove_initial_comment(self):
try:
for comment in self.temp_comments:
self.remove_comment(comment)
except Exception as e:
get_logger().exception(f"Failed to remove temp comments, error: {e}")
def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
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(),
relevant_file.strip('`'),
relevant_line_in_file,
absolute_position)
if position == -1:
if get_settings().config.verbosity_level >= 2:
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()
return dict(body=body, path=path, position=position, absolute_position=absolute_position) if subject_type == "LINE" else {}
def publish_inline_comments(self, comments: list[dict], disable_fallback: bool = False):
overall_sucess = True
for comment in comments:
try:
self.publish_comment(comment["body"],
thread_context={
"filePath": comment["path"],
"rightFileStart": {
"line": comment["absolute_position"],
"offset": comment["position"],
},
"rightFileEnd": {
"line": comment["absolute_position"],
"offset": comment["position"],
},
})
if get_settings().config.verbosity_level >= 2:
get_logger().info(
f"Published code suggestion on {self.pr_num} at {comment['path']}"
)
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to publish code suggestion, error: {e}")
overall_sucess = False
return overall_sucess
def get_title(self):
return self.pr.title
def get_languages(self):
languages = []
files = self.azure_devops_client.get_items(
project=self.workspace_slug,
repository_id=self.repo_slug,
recursion_level="Full",
include_content_metadata=True,
include_links=False,
download=False,
)
for f in files:
if f.git_object_type == "blob":
file_name, file_extension = os.path.splitext(f.path)
languages.append(file_extension[1:])
extension_counts = {}
for ext in languages:
if ext != "":
extension_counts[ext] = extension_counts.get(ext, 0) + 1
total_extensions = sum(extension_counts.values())
extension_percentages = {
ext: (count / total_extensions) * 100
for ext, count in extension_counts.items()
}
return extension_percentages
def get_pr_branch(self):
pr_info = self.azure_devops_client.get_pull_request_by_id(
project=self.workspace_slug, pull_request_id=self.pr_num
)
source_branch = pr_info.source_ref_name.split("/")[-1]
return source_branch
def get_pr_description(self, *, full: bool = True) -> str:
max_tokens = get_settings().get("CONFIG.MAX_DESCRIPTION_TOKENS", None)
if max_tokens:
return clip_tokens(self.pr.description, max_tokens)
return self.pr.description
def get_user_id(self):
return 0
def get_issue_comments(self):
raise NotImplementedError(
"Azure DevOps provider does not support issue comments yet"
)
def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) -> Optional[int]:
return True
def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool:
return True
@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 len(path_parts) < 6 or path_parts[4] != "pullrequest":
raise ValueError(
"The provided URL does not appear to be a Azure DevOps PR URL"
)
workspace_slug = path_parts[1]
repo_slug = path_parts[3]
try:
pr_number = int(path_parts[5])
except ValueError as e:
raise ValueError("Unable to convert PR number to integer") from e
return workspace_slug, repo_slug, pr_number
@staticmethod
def _get_azure_devops_client():
org = get_settings().azure_devops.get("org", None)
pat = get_settings().azure_devops.get("pat", None)
if not org:
raise ValueError("Azure DevOps organization is required")
if pat:
auth_token = pat
else:
try:
# try to use azure default credentials
# see https://learn.microsoft.com/en-us/python/api/overview/azure/identity-readme?view=azure-python
# for usage and env var configuration of user-assigned managed identity, local machine auth etc.
get_logger().info("No PAT found in settings, trying to use Azure Default Credentials.")
credentials = DefaultAzureCredential()
accessToken = credentials.get_token(ADO_APP_CLIENT_DEFAULT_ID)
auth_token = accessToken.token
except Exception as e:
get_logger().error(f"No PAT found in settings, and Azure Default Authentication failed, error: {e}")
raise
credentials = BasicAuthentication("", auth_token)
credentials = BasicAuthentication("", auth_token)
azure_devops_connection = Connection(base_url=org, creds=credentials)
azure_devops_client = azure_devops_connection.clients.get_git_client()
return azure_devops_client
def _get_repo(self):
if self.repo is None:
self.repo = self.azure_devops_client.get_repository(
project=self.workspace_slug, repository_id=self.repo_slug
)
return self.repo
def _get_pr(self):
self.pr = self.azure_devops_client.get_pull_request_by_id(
pull_request_id=self.pr_num, project=self.workspace_slug
)
return self.pr
def get_commit_messages(self):
return "" # not implemented yet
def get_pr_id(self):
try:
pr_id = f"{self.workspace_slug}/{self.repo_slug}/{self.pr_num}"
return pr_id
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to get pr id, error: {e}")
return ""

View File

@ -0,0 +1,368 @@
import json
from typing import Optional, Tuple
from urllib.parse import urlparse
import requests
from atlassian.bitbucket import Cloud
from starlette_context import context
from pr_agent.algo.types import FilePatchInfo, EDIT_TYPE
from ..algo.utils import find_line_number_of_relevant_line_in_file
from ..config_loader import get_settings
from ..log import get_logger
from .git_provider import GitProvider
class BitbucketProvider(GitProvider):
def __init__(
self, pr_url: Optional[str] = None, incremental: Optional[bool] = False
):
s = requests.Session()
try:
bearer = context.get("bitbucket_bearer_token", None)
s.headers["Authorization"] = f"Bearer {bearer}"
except Exception:
s.headers[
"Authorization"
] = f'Bearer {get_settings().get("BITBUCKET.BEARER_TOKEN", None)}'
s.headers["Content-Type"] = "application/json"
self.headers = s.headers
self.bitbucket_client = Cloud(session=s)
self.workspace_slug = None
self.repo_slug = None
self.repo = None
self.pr_num = None
self.pr = None
self.pr_url = pr_url
self.temp_comments = []
self.incremental = incremental
self.diff_files = None
if pr_url:
self.set_pr(pr_url)
self.bitbucket_comment_api_url = self.pr._BitbucketBase__data["links"]["comments"]["href"]
self.bitbucket_pull_request_api_url = self.pr._BitbucketBase__data["links"]['self']['href']
def get_repo_settings(self):
try:
url = (f"https://api.bitbucket.org/2.0/repositories/{self.workspace_slug}/{self.repo_slug}/src/"
f"{self.pr.destination_branch}/.pr_agent.toml")
response = requests.request("GET", url, headers=self.headers)
if response.status_code == 404: # not found
return ""
contents = response.text.encode('utf-8')
return contents
except Exception:
return ""
def publish_code_suggestions(self, code_suggestions: list) -> bool:
"""
Publishes code suggestions as comments on the PR.
"""
post_parameters_list = []
for suggestion in code_suggestions:
body = suggestion["body"]
relevant_file = suggestion["relevant_file"]
relevant_lines_start = suggestion["relevant_lines_start"]
relevant_lines_end = suggestion["relevant_lines_end"]
if not relevant_lines_start or relevant_lines_start == -1:
if get_settings().config.verbosity_level >= 2:
get_logger().exception(
f"Failed to publish code suggestion, relevant_lines_start is {relevant_lines_start}"
)
continue
if relevant_lines_end < relevant_lines_start:
if get_settings().config.verbosity_level >= 2:
get_logger().exception(
f"Failed to publish code suggestion, "
f"relevant_lines_end is {relevant_lines_end} and "
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)
try:
self.publish_inline_comments(post_parameters_list)
return True
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to publish code suggestion, error: {e}")
return False
def is_supported(self, capability: str) -> bool:
if capability in ['get_issue_comments', 'publish_inline_comments', 'get_labels', 'gfm_markdown']:
return False
return True
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()
def get_files(self):
return [diff.new.path for diff in self.pr.diffstat()]
def get_diff_files(self) -> list[FilePatchInfo]:
if self.diff_files:
return self.diff_files
diffs = self.pr.diffstat()
diff_split = [
"diff --git%s" % x for x in self.pr.diff().split("diff --git") if x.strip()
]
diff_files = []
for index, diff in enumerate(diffs):
original_file_content_str = self._get_pr_file_content(
diff.old.get_data("links")
)
new_file_content_str = self._get_pr_file_content(diff.new.get_data("links"))
file_patch_canonic_structure = FilePatchInfo(
original_file_content_str,
new_file_content_str,
diff_split[index],
diff.new.path,
)
if diff.data['status'] == 'added':
file_patch_canonic_structure.edit_type = EDIT_TYPE.ADDED
elif diff.data['status'] == 'removed':
file_patch_canonic_structure.edit_type = EDIT_TYPE.DELETED
elif diff.data['status'] == 'modified':
file_patch_canonic_structure.edit_type = EDIT_TYPE.MODIFIED
elif diff.data['status'] == 'renamed':
file_patch_canonic_structure.edit_type = EDIT_TYPE.RENAMED
diff_files.append(file_patch_canonic_structure)
self.diff_files = diff_files
return diff_files
def get_latest_commit_url(self):
return self.pr.data['source']['commit']['links']['html']['href']
def get_comment_url(self, comment):
return comment.data['links']['html']['href']
def publish_persistent_comment(self, pr_comment: str, initial_header: str, update_header: bool = True, name='review'):
try:
for comment in self.pr.comments():
body = comment.raw
if initial_header in body:
latest_commit_url = self.get_latest_commit_url()
comment_url = self.get_comment_url(comment)
if update_header:
updated_header = f"{initial_header}\n\n### ({name.capitalize()} updated until commit {latest_commit_url})\n"
pr_comment_updated = pr_comment.replace(initial_header, updated_header)
else:
pr_comment_updated = pr_comment
get_logger().info(f"Persistent mode - updating comment {comment_url} to latest {name} message")
d = {"content": {"raw": pr_comment_updated}}
response = comment._update_data(comment.put(None, data=d))
self.publish_comment(
f"**[Persistent {name}]({comment_url})** updated to latest commit {latest_commit_url}")
return
except Exception as e:
get_logger().exception(f"Failed to update persistent review, error: {e}")
pass
self.publish_comment(pr_comment)
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
comment = self.pr.comment(pr_comment)
if is_temporary:
self.temp_comments.append(comment["id"])
return comment
def edit_comment(self, comment, body: str):
try:
comment.update(body)
except Exception as e:
get_logger().exception(f"Failed to update comment, error: {e}")
def remove_initial_comment(self):
try:
for comment in self.temp_comments:
self.remove_comment(comment)
except Exception as e:
get_logger().exception(f"Failed to remove temp comments, error: {e}")
def remove_comment(self, comment):
try:
self.pr.delete(f"comments/{comment}")
except Exception as e:
get_logger().exception(f"Failed to remove comment, error: {e}")
# funtion to create_inline_comment
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(),
relevant_file.strip('`'),
relevant_line_in_file, absolute_position)
if position == -1:
if get_settings().config.verbosity_level >= 2:
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()
return dict(body=body, path=path, position=absolute_position) if subject_type == "LINE" else {}
def publish_inline_comment(self, comment: str, from_line: int, file: str):
payload = json.dumps( {
"content": {
"raw": comment,
},
"inline": {
"to": from_line,
"path": file
},
})
response = requests.request(
"POST", self.bitbucket_comment_api_url, data=payload, headers=self.headers
)
return response
def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str:
if relevant_line_start == -1:
link = f"{self.pr_url}/#L{relevant_file}"
else:
link = f"{self.pr_url}/#L{relevant_file}T{relevant_line_start}"
return link
def generate_link_to_relevant_line_number(self, suggestion) -> str:
try:
relevant_file = suggestion['relevant_file'].strip('`').strip("'").rstrip()
relevant_line_str = suggestion['relevant_line'].rstrip()
if not relevant_line_str:
return ""
diff_files = self.get_diff_files()
position, absolute_position = find_line_number_of_relevant_line_in_file \
(diff_files, relevant_file, relevant_line_str)
if absolute_position != -1 and self.pr_url:
link = f"{self.pr_url}/#L{relevant_file}T{absolute_position}"
return link
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"Failed adding line link, error: {e}")
return ""
def publish_inline_comments(self, comments: list[dict]):
for comment in comments:
if 'position' in comment:
self.publish_inline_comment(comment['body'], comment['position'], comment['path'])
elif 'start_line' in comment: # multi-line comment
# note that bitbucket does not seem to support range - only a comment on a single line - https://community.developer.atlassian.com/t/api-post-endpoint-for-inline-pull-request-comments/60452
self.publish_inline_comment(comment['body'], comment['start_line'], comment['path'])
elif 'line' in comment: # single-line comment
self.publish_inline_comment(comment['body'], comment['line'], comment['path'])
else:
get_logger().error(f"Could not publish inline comment {comment}")
def get_title(self):
return self.pr.title
def get_languages(self):
languages = {self._get_repo().get_data("language"): 0}
return languages
def get_pr_branch(self):
return self.pr.source_branch
def get_pr_description_full(self):
return self.pr.description
def get_user_id(self):
return 0
def get_issue_comments(self):
raise NotImplementedError(
"Bitbucket provider does not support issue comments yet"
)
def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) -> Optional[int]:
return True
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]:
parsed_url = urlparse(pr_url)
if "bitbucket.org" not in parsed_url.netloc:
raise ValueError("The provided URL is not a valid Bitbucket URL")
path_parts = parsed_url.path.strip("/").split("/")
if len(path_parts) < 4 or path_parts[2] != "pull-requests":
raise ValueError(
"The provided URL does not appear to be a Bitbucket PR URL"
)
workspace_slug = path_parts[0]
repo_slug = path_parts[1]
try:
pr_number = int(path_parts[3])
except ValueError as e:
raise ValueError("Unable to convert PR number to integer") from e
return workspace_slug, repo_slug, pr_number
def _get_repo(self):
if self.repo is None:
self.repo = self.bitbucket_client.workspaces.get(
self.workspace_slug
).repositories.get(self.repo_slug)
return self.repo
def _get_pr(self):
return self._get_repo().pullrequests.get(self.pr_num)
def _get_pr_file_content(self, remote_link: str):
return ""
def get_commit_messages(self):
return "" # not implemented yet
# bitbucket does not support labels
def publish_description(self, pr_title: str, description: str):
payload = json.dumps({
"description": description,
"title": pr_title
})
response = requests.request("PUT", self.bitbucket_pull_request_api_url, headers=self.headers, data=payload)
try:
if response.status_code != 200:
get_logger().info(f"Failed to update description, error code: {response.status_code}")
except:
pass
return response
# bitbucket does not support labels
def publish_labels(self, pr_types: list):
pass
# bitbucket does not support labels
def get_pr_labels(self):
pass

View File

@ -0,0 +1,354 @@
import json
from typing import Optional, Tuple
from urllib.parse import urlparse
import requests
from atlassian.bitbucket import Bitbucket
from starlette_context import context
from .git_provider import GitProvider
from pr_agent.algo.types import FilePatchInfo
from ..algo.utils import load_large_diff, find_line_number_of_relevant_line_in_file
from ..config_loader import get_settings
from ..log import get_logger
class BitbucketServerProvider(GitProvider):
def __init__(
self, pr_url: Optional[str] = None, incremental: Optional[bool] = False
):
s = requests.Session()
try:
bearer = context.get("bitbucket_bearer_token", None)
s.headers["Authorization"] = f"Bearer {bearer}"
except Exception:
s.headers[
"Authorization"
] = f'Bearer {get_settings().get("BITBUCKET_SERVER.BEARER_TOKEN", None)}'
s.headers["Content-Type"] = "application/json"
self.headers = s.headers
self.bitbucket_server_url = None
self.workspace_slug = None
self.repo_slug = None
self.repo = None
self.pr_num = None
self.pr = None
self.pr_url = pr_url
self.temp_comments = []
self.incremental = incremental
self.diff_files = None
self.bitbucket_pull_request_api_url = pr_url
self.bitbucket_server_url = self._parse_bitbucket_server(url=pr_url)
self.bitbucket_client = Bitbucket(url=self.bitbucket_server_url,
token=get_settings().get("BITBUCKET_SERVER.BEARER_TOKEN", None))
if pr_url:
self.set_pr(pr_url)
def get_repo_settings(self):
try:
url = (f"{self.bitbucket_server_url}/projects/{self.workspace_slug}/repos/{self.repo_slug}/src/"
f"{self.pr.destination_branch}/.pr_agent.toml")
response = requests.request("GET", url, headers=self.headers)
if response.status_code == 404: # not found
return ""
contents = response.text.encode('utf-8')
return contents
except Exception:
return ""
def publish_code_suggestions(self, code_suggestions: list) -> bool:
"""
Publishes code suggestions as comments on the PR.
"""
post_parameters_list = []
for suggestion in code_suggestions:
body = suggestion["body"]
relevant_file = suggestion["relevant_file"]
relevant_lines_start = suggestion["relevant_lines_start"]
relevant_lines_end = suggestion["relevant_lines_end"]
if not relevant_lines_start or relevant_lines_start == -1:
if get_settings().config.verbosity_level >= 2:
get_logger().exception(
f"Failed to publish code suggestion, relevant_lines_start is {relevant_lines_start}"
)
continue
if relevant_lines_end < relevant_lines_start:
if get_settings().config.verbosity_level >= 2:
get_logger().exception(
f"Failed to publish code suggestion, "
f"relevant_lines_end is {relevant_lines_end} and "
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)
try:
self.publish_inline_comments(post_parameters_list)
return True
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to publish code suggestion, error: {e}")
return False
def is_supported(self, capability: str) -> bool:
if capability in ['get_issue_comments', 'get_labels', 'gfm_markdown']:
return False
return True
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()
def get_file(self, path: str, commit_id: str):
file_content = ""
try:
file_content = self.bitbucket_client.get_content_of_file(self.workspace_slug,
self.repo_slug,
path,
commit_id)
except requests.HTTPError as e:
get_logger().debug(f"File {path} not found at commit id: {commit_id}")
return file_content
def get_files(self):
changes = self.bitbucket_client.get_pull_requests_changes(self.workspace_slug, self.repo_slug, self.pr_num)
diffstat = [change["path"]['toString'] for change in changes]
return diffstat
def get_diff_files(self) -> list[FilePatchInfo]:
if self.diff_files:
return self.diff_files
commits_in_pr = self.bitbucket_client.get_pull_requests_commits(
self.workspace_slug,
self.repo_slug,
self.pr_num
)
commit_list = list(commits_in_pr)
base_sha, head_sha = commit_list[0]['parents'][0]['id'], commit_list[-1]['id']
diff_files = []
original_file_content_str = ""
new_file_content_str = ""
changes = self.bitbucket_client.get_pull_requests_changes(self.workspace_slug, self.repo_slug, self.pr_num)
for change in changes:
file_path = change['path']['toString']
match change['type']:
case 'ADD':
edit_type = EDIT_TYPE.ADDED
new_file_content_str = self.get_file(file_path, head_sha)
if isinstance(new_file_content_str, (bytes, bytearray)):
new_file_content_str = new_file_content_str.decode("utf-8")
original_file_content_str = ""
case 'DELETE':
edit_type = EDIT_TYPE.DELETED
new_file_content_str = ""
original_file_content_str = self.get_file(file_path, base_sha)
if isinstance(original_file_content_str, (bytes, bytearray)):
original_file_content_str = original_file_content_str.decode("utf-8")
case 'RENAME':
edit_type = EDIT_TYPE.RENAMED
case _:
edit_type = EDIT_TYPE.MODIFIED
original_file_content_str = self.get_file(file_path, base_sha)
if isinstance(original_file_content_str, (bytes, bytearray)):
original_file_content_str = original_file_content_str.decode("utf-8")
new_file_content_str = self.get_file(file_path, head_sha)
if isinstance(new_file_content_str, (bytes, bytearray)):
new_file_content_str = new_file_content_str.decode("utf-8")
patch = load_large_diff(file_path, new_file_content_str, original_file_content_str)
diff_files.append(
FilePatchInfo(
original_file_content_str,
new_file_content_str,
patch,
file_path,
edit_type=edit_type,
)
)
self.diff_files = diff_files
return diff_files
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
if not is_temporary:
self.bitbucket_client.add_pull_request_comment(self.workspace_slug, self.repo_slug, self.pr_num, pr_comment)
def remove_initial_comment(self):
try:
for comment in self.temp_comments:
self.remove_comment(comment)
except ValueError as e:
get_logger().exception(f"Failed to remove temp comments, error: {e}")
def remove_comment(self, comment):
pass
# funtion to create_inline_comment
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(),
relevant_file.strip('`'),
relevant_line_in_file,
absolute_position
)
if position == -1:
if get_settings().config.verbosity_level >= 2:
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()
return dict(body=body, path=path, position=absolute_position) if subject_type == "LINE" else {}
def publish_inline_comment(self, comment: str, from_line: int, file: str):
payload = {
"text": comment,
"severity": "NORMAL",
"anchor": {
"diffType": "EFFECTIVE",
"path": file,
"lineType": "ADDED",
"line": from_line,
"fileType": "TO"
}
}
response = requests.post(url=self._get_pr_comments_url(), json=payload, headers=self.headers)
return response
def generate_link_to_relevant_line_number(self, suggestion) -> str:
try:
relevant_file = suggestion['relevant_file'].strip('`').strip("'").rstrip()
relevant_line_str = suggestion['relevant_line'].rstrip()
if not relevant_line_str:
return ""
diff_files = self.get_diff_files()
position, absolute_position = find_line_number_of_relevant_line_in_file \
(diff_files, relevant_file, relevant_line_str)
if absolute_position != -1 and self.pr_url:
link = f"{self.pr_url}/#L{relevant_file}T{absolute_position}"
return link
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"Failed adding line link, error: {e}")
return ""
def publish_inline_comments(self, comments: list[dict]):
for comment in comments:
self.publish_inline_comment(comment['body'], comment['position'], comment['path'])
def get_title(self):
return self.pr.title
def get_languages(self):
return {"yaml": 0} # devops LOL
def get_pr_branch(self):
return self.pr.fromRef['displayId']
def get_pr_description_full(self):
return self.pr.description
def get_user_id(self):
return 0
def get_issue_comments(self):
raise NotImplementedError(
"Bitbucket provider does not support issue comments yet"
)
def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) -> Optional[int]:
return True
def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool:
return True
@staticmethod
def _parse_bitbucket_server(url: str) -> str:
parsed_url = urlparse(url)
return f"{parsed_url.scheme}://{parsed_url.netloc}"
@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 len(path_parts) < 6 or path_parts[4] != "pull-requests":
raise ValueError(
"The provided URL does not appear to be a Bitbucket PR URL"
)
workspace_slug = path_parts[1]
repo_slug = path_parts[3]
try:
pr_number = int(path_parts[5])
except ValueError as e:
raise ValueError("Unable to convert PR number to integer") from e
return workspace_slug, repo_slug, pr_number
def _get_repo(self):
if self.repo is None:
self.repo = self.bitbucket_client.get_repo(self.workspace_slug, self.repo_slug)
return self.repo
def _get_pr(self):
pr = self.bitbucket_client.get_pull_request(self.workspace_slug, self.repo_slug, pull_request_id=self.pr_num)
return type('new_dict', (object,), pr)
def _get_pr_file_content(self, remote_link: str):
return ""
def get_commit_messages(self):
def get_commit_messages(self):
raise NotImplementedError("Get commit messages function not implemented yet.")
# bitbucket does not support labels
def publish_description(self, pr_title: str, description: str):
payload = json.dumps({
"description": description,
"title": pr_title
})
response = requests.put(url=self.bitbucket_pull_request_api_url, headers=self.headers, data=payload)
return response
# bitbucket does not support labels
def publish_labels(self, pr_types: list):
pass
# bitbucket does not support labels
def get_pr_labels(self):
pass
def _get_pr_comments_url(self):
return f"{self.bitbucket_server_url}/rest/api/latest/projects/{self.workspace_slug}/repos/{self.repo_slug}/pull-requests/{self.pr_num}/comments"

View File

@ -0,0 +1,277 @@
import boto3
import botocore
class CodeCommitDifferencesResponse:
"""
CodeCommitDifferencesResponse is the response object returned from our get_differences() function.
It maps the JSON response to member variables of this class.
"""
def __init__(self, json: dict):
before_blob = json.get("beforeBlob", {})
after_blob = json.get("afterBlob", {})
self.before_blob_id = before_blob.get("blobId", "")
self.before_blob_path = before_blob.get("path", "")
self.after_blob_id = after_blob.get("blobId", "")
self.after_blob_path = after_blob.get("path", "")
self.change_type = json.get("changeType", "")
class CodeCommitPullRequestResponse:
"""
CodeCommitPullRequestResponse is the response object returned from our get_pr() function.
It maps the JSON response to member variables of this class.
"""
def __init__(self, json: dict):
self.title = json.get("title", "")
self.description = json.get("description", "")
self.targets = []
for target in json.get("pullRequestTargets", []):
self.targets.append(CodeCommitPullRequestResponse.CodeCommitPullRequestTarget(target))
class CodeCommitPullRequestTarget:
"""
CodeCommitPullRequestTarget is a subclass of CodeCommitPullRequestResponse that
holds details about an individual target commit.
"""
def __init__(self, json: dict):
self.source_commit = json.get("sourceCommit", "")
self.source_branch = json.get("sourceReference", "")
self.destination_commit = json.get("destinationCommit", "")
self.destination_branch = json.get("destinationReference", "")
class CodeCommitClient:
"""
CodeCommitClient is a wrapper around the AWS boto3 SDK for the CodeCommit client
"""
def __init__(self):
self.boto_client = None
def is_supported(self, capability: str) -> bool:
if capability in ["gfm_markdown"]:
return False
return True
def _connect_boto_client(self):
try:
self.boto_client = boto3.client("codecommit")
except Exception as e:
raise ValueError(f"Failed to connect to AWS CodeCommit: {e}") from e
def get_differences(self, repo_name: int, destination_commit: str, source_commit: str):
"""
Get the differences between two commits in CodeCommit.
Args:
- repo_name: Name of the repository
- destination_commit: Commit hash you want to merge into (the "before" hash) (usually on the main or master branch)
- source_commit: Commit hash of the code you are adding (the "after" branch)
Returns:
- List of CodeCommitDifferencesResponse objects
Boto3 Documentation:
- aws codecommit get-differences
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codecommit/client/get_differences.html
"""
if self.boto_client is None:
self._connect_boto_client()
# The differences response from AWS is paginated, so we need to iterate through the pages to get all the differences.
differences = []
try:
paginator = self.boto_client.get_paginator("get_differences")
for page in paginator.paginate(
repositoryName=repo_name,
beforeCommitSpecifier=destination_commit,
afterCommitSpecifier=source_commit,
):
differences.extend(page.get("differences", []))
except botocore.exceptions.ClientError as e:
if e.response["Error"]["Code"] == 'RepositoryDoesNotExistException':
raise ValueError(f"CodeCommit cannot retrieve differences: Repository does not exist: {repo_name}") from e
raise ValueError(f"CodeCommit cannot retrieve differences for {source_commit}..{destination_commit}") from e
except Exception as e:
raise ValueError(f"CodeCommit cannot retrieve differences for {source_commit}..{destination_commit}") from e
output = []
for json in differences:
output.append(CodeCommitDifferencesResponse(json))
return output
def get_file(self, repo_name: str, file_path: str, sha_hash: str, optional: bool = False):
"""
Retrieve a file from CodeCommit.
Args:
- repo_name: Name of the repository
- file_path: Path to the file you are retrieving
- sha_hash: Commit hash of the file you are retrieving
Returns:
- File contents
Boto3 Documentation:
- aws codecommit get_file
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codecommit/client/get_file.html
"""
if not file_path:
return ""
if self.boto_client is None:
self._connect_boto_client()
try:
response = self.boto_client.get_file(repositoryName=repo_name, commitSpecifier=sha_hash, filePath=file_path)
except botocore.exceptions.ClientError as e:
if e.response["Error"]["Code"] == 'RepositoryDoesNotExistException':
raise ValueError(f"CodeCommit cannot retrieve PR: Repository does not exist: {repo_name}") from e
# if the file does not exist, but is flagged as optional, then return an empty string
if optional and e.response["Error"]["Code"] == 'FileDoesNotExistException':
return ""
raise ValueError(f"CodeCommit cannot retrieve file '{file_path}' from repository '{repo_name}'") from e
except Exception as e:
raise ValueError(f"CodeCommit cannot retrieve file '{file_path}' from repository '{repo_name}'") from e
if "fileContent" not in response:
raise ValueError(f"File content is empty for file: {file_path}")
return response.get("fileContent", "")
def get_pr(self, repo_name: str, pr_number: int):
"""
Get a information about a CodeCommit PR.
Args:
- repo_name: Name of the repository
- pr_number: The PR number you are requesting
Returns:
- CodeCommitPullRequestResponse object
Boto3 Documentation:
- aws codecommit get_pull_request
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codecommit/client/get_pull_request.html
"""
if self.boto_client is None:
self._connect_boto_client()
try:
response = self.boto_client.get_pull_request(pullRequestId=str(pr_number))
except botocore.exceptions.ClientError as e:
if e.response["Error"]["Code"] == 'PullRequestDoesNotExistException':
raise ValueError(f"CodeCommit cannot retrieve PR: PR number does not exist: {pr_number}") from e
if e.response["Error"]["Code"] == 'RepositoryDoesNotExistException':
raise ValueError(f"CodeCommit cannot retrieve PR: Repository does not exist: {repo_name}") from e
raise ValueError(f"CodeCommit cannot retrieve PR: {pr_number}: boto client error") from e
except Exception as e:
raise ValueError(f"CodeCommit cannot retrieve PR: {pr_number}") from e
if "pullRequest" not in response:
raise ValueError("CodeCommit PR number not found: {pr_number}")
return CodeCommitPullRequestResponse(response.get("pullRequest", {}))
def publish_description(self, pr_number: int, pr_title: str, pr_body: str):
"""
Set the title and description on a pull request
Args:
- pr_number: the AWS CodeCommit pull request number
- pr_title: title of the pull request
- pr_body: body of the pull request
Returns:
- None
Boto3 Documentation:
- aws codecommit update_pull_request_title
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codecommit/client/update_pull_request_title.html
- aws codecommit update_pull_request_description
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codecommit/client/update_pull_request_description.html
"""
if self.boto_client is None:
self._connect_boto_client()
try:
self.boto_client.update_pull_request_title(pullRequestId=str(pr_number), title=pr_title)
self.boto_client.update_pull_request_description(pullRequestId=str(pr_number), description=pr_body)
except botocore.exceptions.ClientError as e:
if e.response["Error"]["Code"] == 'PullRequestDoesNotExistException':
raise ValueError(f"PR number does not exist: {pr_number}") from e
if e.response["Error"]["Code"] == 'InvalidTitleException':
raise ValueError(f"Invalid title for PR number: {pr_number}") from e
if e.response["Error"]["Code"] == 'InvalidDescriptionException':
raise ValueError(f"Invalid description for PR number: {pr_number}") from e
if e.response["Error"]["Code"] == 'PullRequestAlreadyClosedException':
raise ValueError(f"PR is already closed: PR number: {pr_number}") from e
raise ValueError(f"Boto3 client error calling publish_description") from e
except Exception as e:
raise ValueError(f"Error calling publish_description") from e
def publish_comment(self, repo_name: str, pr_number: int, destination_commit: str, source_commit: str, comment: str, annotation_file: str = None, annotation_line: int = None):
"""
Publish a comment to a pull request
Args:
- repo_name: name of the repository
- pr_number: number of the pull request
- destination_commit: The commit hash you want to merge into (the "before" hash) (usually on the main or master branch)
- source_commit: The commit hash of the code you are adding (the "after" branch)
- comment: The comment you want to publish
- annotation_file: The file you want to annotate (optional)
- annotation_line: The line number you want to annotate (optional)
Comment annotations for CodeCommit are different than GitHub.
CodeCommit only designates the starting line number for the comment.
It does not support the ending line number to highlight a range of lines.
Returns:
- None
Boto3 Documentation:
- aws codecommit post_comment_for_pull_request
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codecommit/client/post_comment_for_pull_request.html
"""
if self.boto_client is None:
self._connect_boto_client()
try:
# If the comment has code annotations,
# then set the file path and line number in the location dictionary
if annotation_file and annotation_line:
self.boto_client.post_comment_for_pull_request(
pullRequestId=str(pr_number),
repositoryName=repo_name,
beforeCommitId=destination_commit,
afterCommitId=source_commit,
content=comment,
location={
"filePath": annotation_file,
"filePosition": annotation_line,
"relativeFileVersion": "AFTER",
},
)
else:
# The comment does not have code annotations
self.boto_client.post_comment_for_pull_request(
pullRequestId=str(pr_number),
repositoryName=repo_name,
beforeCommitId=destination_commit,
afterCommitId=source_commit,
content=comment,
)
except botocore.exceptions.ClientError as e:
if e.response["Error"]["Code"] == 'RepositoryDoesNotExistException':
raise ValueError(f"Repository does not exist: {repo_name}") from e
if e.response["Error"]["Code"] == 'PullRequestDoesNotExistException':
raise ValueError(f"PR number does not exist: {pr_number}") from e
raise ValueError(f"Boto3 client error calling post_comment_for_pull_request") from e
except Exception as e:
raise ValueError(f"Error calling post_comment_for_pull_request") from e

View File

@ -0,0 +1,495 @@
import os
import re
from collections import Counter
from typing import List, Optional, Tuple
from urllib.parse import urlparse
from pr_agent.git_providers.codecommit_client import CodeCommitClient
from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
from ..algo.utils import load_large_diff
from .git_provider import GitProvider
from ..config_loader import get_settings
from ..log import get_logger
class PullRequestCCMimic:
"""
This class mimics the PullRequest class from the PyGithub library for the CodeCommitProvider.
"""
def __init__(self, title: str, diff_files: List[FilePatchInfo]):
self.title = title
self.diff_files = diff_files
self.description = None
self.source_commit = None
self.source_branch = None # the branch containing your new code changes
self.destination_commit = None
self.destination_branch = None # the branch you are going to merge into
class CodeCommitFile:
"""
This class represents a file in a pull request in CodeCommit.
"""
def __init__(
self,
a_path: str,
a_blob_id: str,
b_path: str,
b_blob_id: str,
edit_type: EDIT_TYPE,
):
self.a_path = a_path
self.a_blob_id = a_blob_id
self.b_path = b_path
self.b_blob_id = b_blob_id
self.edit_type: EDIT_TYPE = edit_type
self.filename = b_path if b_path else a_path
class CodeCommitProvider(GitProvider):
"""
This class implements the GitProvider interface for AWS CodeCommit repositories.
"""
def __init__(self, pr_url: Optional[str] = None, incremental: Optional[bool] = False):
self.codecommit_client = CodeCommitClient()
self.aws_client = None
self.repo_name = None
self.pr_num = None
self.pr = None
self.diff_files = None
self.git_files = None
self.pr_url = pr_url
if pr_url:
self.set_pr(pr_url)
def provider_name(self):
return "CodeCommit"
def is_supported(self, capability: str) -> bool:
if capability in [
"get_issue_comments",
"create_inline_comment",
"publish_inline_comments",
"get_labels",
"gfm_markdown"
]:
return False
return True
def set_pr(self, pr_url: str):
self.repo_name, self.pr_num = self._parse_pr_url(pr_url)
self.pr = self._get_pr()
def get_files(self) -> list[CodeCommitFile]:
# bring files from CodeCommit only once
if self.git_files:
return self.git_files
self.git_files = []
differences = self.codecommit_client.get_differences(self.repo_name, self.pr.destination_commit, self.pr.source_commit)
for item in differences:
self.git_files.append(CodeCommitFile(item.before_blob_path,
item.before_blob_id,
item.after_blob_path,
item.after_blob_id,
CodeCommitProvider._get_edit_type(item.change_type)))
return self.git_files
def get_diff_files(self) -> list[FilePatchInfo]:
"""
Retrieves the list of files that have been modified, added, deleted, or renamed in a pull request in CodeCommit,
along with their content and patch information.
Returns:
diff_files (List[FilePatchInfo]): List of FilePatchInfo objects representing the modified, added, deleted,
or renamed files in the merge request.
"""
# bring files from CodeCommit only once
if self.diff_files:
return self.diff_files
self.diff_files = []
files = self.get_files()
for diff_item in files:
patch_filename = ""
if diff_item.a_blob_id is not None:
patch_filename = diff_item.a_path
original_file_content_str = self.codecommit_client.get_file(
self.repo_name, diff_item.a_path, self.pr.destination_commit)
if isinstance(original_file_content_str, (bytes, bytearray)):
original_file_content_str = original_file_content_str.decode("utf-8")
else:
original_file_content_str = ""
if diff_item.b_blob_id is not None:
patch_filename = diff_item.b_path
new_file_content_str = self.codecommit_client.get_file(self.repo_name, diff_item.b_path, self.pr.source_commit)
if isinstance(new_file_content_str, (bytes, bytearray)):
new_file_content_str = new_file_content_str.decode("utf-8")
else:
new_file_content_str = ""
patch = load_large_diff(patch_filename, new_file_content_str, original_file_content_str)
# Store the diffs as a list of FilePatchInfo objects
info = FilePatchInfo(
original_file_content_str,
new_file_content_str,
patch,
diff_item.b_path,
edit_type=diff_item.edit_type,
old_filename=None
if diff_item.a_path == diff_item.b_path
else diff_item.a_path,
)
# Only add valid files to the diff list
# "bad extensions" are set in the language_extensions.toml file
# a "valid file" is one that is not in the "bad extensions" list
if is_valid_file(info.filename):
self.diff_files.append(info)
return self.diff_files
def publish_description(self, pr_title: str, pr_body: str):
try:
self.codecommit_client.publish_description(
pr_number=self.pr_num,
pr_title=pr_title,
pr_body=CodeCommitProvider._add_additional_newlines(pr_body),
)
except Exception as e:
raise ValueError(f"CodeCommit Cannot publish description for PR: {self.pr_num}") from e
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
if is_temporary:
get_logger().info(pr_comment)
return
pr_comment = CodeCommitProvider._remove_markdown_html(pr_comment)
pr_comment = CodeCommitProvider._add_additional_newlines(pr_comment)
try:
self.codecommit_client.publish_comment(
repo_name=self.repo_name,
pr_number=self.pr_num,
destination_commit=self.pr.destination_commit,
source_commit=self.pr.source_commit,
comment=pr_comment,
)
except Exception as e:
raise ValueError(f"CodeCommit Cannot publish comment for PR: {self.pr_num}") from e
def publish_code_suggestions(self, code_suggestions: list) -> bool:
counter = 1
for suggestion in code_suggestions:
# Verify that each suggestion has the required keys
if not all(key in suggestion for key in ["body", "relevant_file", "relevant_lines_start"]):
get_logger().warning(f"Skipping code suggestion #{counter}: Each suggestion must have 'body', 'relevant_file', 'relevant_lines_start' keys")
continue
# Publish the code suggestion to CodeCommit
try:
get_logger().debug(f"Code Suggestion #{counter} in file: {suggestion['relevant_file']}: {suggestion['relevant_lines_start']}")
self.codecommit_client.publish_comment(
repo_name=self.repo_name,
pr_number=self.pr_num,
destination_commit=self.pr.destination_commit,
source_commit=self.pr.source_commit,
comment=suggestion["body"],
annotation_file=suggestion["relevant_file"],
annotation_line=suggestion["relevant_lines_start"],
)
except Exception as e:
raise ValueError(f"CodeCommit Cannot publish code suggestions for PR: {self.pr_num}") from e
counter += 1
# The calling function passes in a list of code suggestions, and this function publishes each suggestion one at a time.
# If we were to return False here, the calling function will attempt to publish the same list of code suggestions again, one at a time.
# Since this function publishes the suggestions one at a time anyway, we always return True here to avoid the retry.
return True
def publish_labels(self, labels):
return [""] # not implemented yet
def get_pr_labels(self):
return [""] # not implemented yet
def remove_initial_comment(self):
return "" # not implemented yet
def remove_comment(self, comment):
return "" # not implemented yet
def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codecommit/client/post_comment_for_compared_commit.html
raise NotImplementedError("CodeCommit provider does not support publishing inline comments yet")
def publish_inline_comments(self, comments: list[dict]):
raise NotImplementedError("CodeCommit provider does not support publishing inline comments yet")
def get_title(self):
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.
Returns:
- dict: A dictionary where each key is a language name and the corresponding value is the percentage of that language in the PR.
"""
commit_files = self.get_files()
filenames = [ item.filename for item in commit_files ]
extensions = CodeCommitProvider._get_file_extensions(filenames)
# Calculate the percentage of each file extension in the PR
percentages = CodeCommitProvider._get_language_percentages(extensions)
# The global language_extension_map is a dictionary of languages,
# where each dictionary item is a BoxList of extensions.
# We want a dictionary of extensions,
# where each dictionary item is a language name.
# We build that language->extension dictionary here in main_extensions_flat.
main_extensions_flat = {}
language_extension_map_org = get_settings().language_extension_map_org
language_extension_map = {k.lower(): v for k, v in language_extension_map_org.items()}
for language, extensions in language_extension_map.items():
for ext in extensions:
main_extensions_flat[ext] = language
# Map the file extension/languages to percentages
languages = {}
for ext, pct in percentages.items():
languages[main_extensions_flat.get(ext, "")] = pct
return languages
def get_pr_branch(self):
return self.pr.source_branch
def get_pr_description_full(self) -> str:
return self.pr.description
def get_user_id(self):
return -1 # not implemented yet
def get_issue_comments(self):
raise NotImplementedError("CodeCommit provider does not support issue comments yet")
def get_repo_settings(self):
# a local ".pr_agent.toml" settings file is optional
settings_filename = ".pr_agent.toml"
return self.codecommit_client.get_file(self.repo_name, settings_filename, self.pr.source_commit, optional=True)
def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) -> Optional[int]:
get_logger().info("CodeCommit provider does not support eyes reaction yet")
return True
def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool:
get_logger().info("CodeCommit provider does not support removing reactions yet")
return True
@staticmethod
def _parse_pr_url(pr_url: str) -> Tuple[str, int]:
"""
Parse the CodeCommit PR URL and return the repository name and PR number.
Args:
- pr_url: the full AWS CodeCommit pull request URL
Returns:
- Tuple[str, int]: A tuple containing the repository name and PR number.
"""
# Example PR URL:
# https://us-east-1.console.aws.amazon.com/codesuite/codecommit/repositories/__MY_REPO__/pull-requests/123456"
parsed_url = urlparse(pr_url)
if not CodeCommitProvider._is_valid_codecommit_hostname(parsed_url.netloc):
raise ValueError(f"The provided URL is not a valid CodeCommit URL: {pr_url}")
path_parts = parsed_url.path.strip("/").split("/")
if (
len(path_parts) < 6
or path_parts[0] != "codesuite"
or path_parts[1] != "codecommit"
or path_parts[2] != "repositories"
or path_parts[4] != "pull-requests"
):
raise ValueError(f"The provided URL does not appear to be a CodeCommit PR URL: {pr_url}")
repo_name = path_parts[3]
try:
pr_number = int(path_parts[5])
except ValueError as e:
raise ValueError(f"Unable to convert PR number to integer: '{path_parts[5]}'") from e
return repo_name, pr_number
@staticmethod
def _is_valid_codecommit_hostname(hostname: str) -> bool:
"""
Check if the provided hostname is a valid AWS CodeCommit hostname.
This is not an exhaustive check of AWS region names,
but instead uses a regex to check for matching AWS region patterns.
Args:
- hostname: the hostname to check
Returns:
- bool: True if the hostname is valid, False otherwise.
"""
return re.match(r"^[a-z]{2}-(gov-)?[a-z]+-\d\.console\.aws\.amazon\.com$", hostname) is not None
def _get_pr(self):
response = self.codecommit_client.get_pr(self.repo_name, self.pr_num)
if len(response.targets) == 0:
raise ValueError(f"No files found in CodeCommit PR: {self.pr_num}")
# TODO: implement support for multiple targets in one CodeCommit PR
# for now, we are only using the first target in the PR
if len(response.targets) > 1:
get_logger().warning(
"Multiple targets in one PR is not supported for CodeCommit yet. Continuing, using the first target only..."
)
# Return our object that mimics PullRequest class from the PyGithub library
# (This strategy was copied from the LocalGitProvider)
mimic = PullRequestCCMimic(response.title, self.diff_files)
mimic.description = response.description
mimic.source_commit = response.targets[0].source_commit
mimic.source_branch = response.targets[0].source_branch
mimic.destination_commit = response.targets[0].destination_commit
mimic.destination_branch = response.targets[0].destination_branch
return mimic
def get_commit_messages(self):
return "" # not implemented yet
@staticmethod
def _add_additional_newlines(body: str) -> str:
"""
Replace single newlines in a PR body with double newlines.
CodeCommit Markdown does not seem to render as well as GitHub Markdown,
so we add additional newlines to the PR body to make it more readable in CodeCommit.
Args:
- body: the PR body
Returns:
- str: the PR body with the double newlines added
"""
return re.sub(r'(?<!\n)\n(?!\n)', '\n\n', body)
@staticmethod
def _remove_markdown_html(comment: str) -> str:
"""
Remove the HTML tags from a PR comment.
CodeCommit Markdown does not seem to render as well as GitHub Markdown,
so we remove the HTML tags from the PR comment to make it more readable in CodeCommit.
Args:
- comment: the PR comment
Returns:
- str: the PR comment with the HTML tags removed
"""
comment = comment.replace("<details>", "")
comment = comment.replace("</details>", "")
comment = comment.replace("<summary>", "")
comment = comment.replace("</summary>", "")
return comment
@staticmethod
def _get_edit_type(codecommit_change_type: str):
"""
Convert the CodeCommit change type string to the EDIT_TYPE enum.
The CodeCommit change type string is returned from the get_differences SDK method.
Args:
- codecommit_change_type: the CodeCommit change type string
Returns:
- An EDIT_TYPE enum representing the modified, added, deleted, or renamed file in the PR diff.
"""
t = codecommit_change_type.upper()
edit_type = None
if t == "A":
edit_type = EDIT_TYPE.ADDED
elif t == "D":
edit_type = EDIT_TYPE.DELETED
elif t == "M":
edit_type = EDIT_TYPE.MODIFIED
elif t == "R":
edit_type = EDIT_TYPE.RENAMED
return edit_type
@staticmethod
def _get_file_extensions(filenames):
"""
Return a list of file extensions from a list of filenames.
The returned extensions will include the dot "." prefix,
to accommodate for the dots in the existing language_extension_map settings.
Filenames with no extension will return an empty string for the extension.
Args:
- filenames: a list of filenames
Returns:
- list: A list of file extensions, including the dot "." prefix.
"""
extensions = []
for filename in filenames:
filename, ext = os.path.splitext(filename)
if ext:
extensions.append(ext.lower())
else:
extensions.append("")
return extensions
@staticmethod
def _get_language_percentages(extensions):
"""
Return a dictionary containing the programming language name (as the key),
and the percentage that language is used (as the value),
given a list of file extensions.
Args:
- extensions: a list of file extensions
Returns:
- dict: A dictionary where each key is a language name and the corresponding value is the percentage of that language in the PR.
"""
total_files = len(extensions)
if total_files == 0:
return {}
# Identify language by file extension and count
lang_count = Counter(extensions)
# Convert counts to percentages
lang_percentage = {
lang: round(count / total_files * 100) for lang, count in lang_count.items()
}
return lang_percentage

View File

@ -0,0 +1,399 @@
import json
import os
import pathlib
import shutil
import subprocess
import uuid
from collections import Counter, namedtuple
from pathlib import Path
from tempfile import NamedTemporaryFile, mkdtemp
import requests
import urllib3.util
from git import Repo
from pr_agent.config_loader import get_settings
from pr_agent.git_providers.git_provider import GitProvider
from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
from pr_agent.git_providers.local_git_provider import PullRequestMimic
from pr_agent.log import get_logger
def _call(*command, **kwargs) -> (int, str, str):
res = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=True,
**kwargs,
)
return res.stdout.decode()
def clone(url, directory):
get_logger().info("Cloning %s to %s", url, directory)
stdout = _call('git', 'clone', "--depth", "1", url, directory)
get_logger().info(stdout)
def fetch(url, refspec, cwd):
get_logger().info("Fetching %s %s", url, refspec)
stdout = _call(
'git', 'fetch', '--depth', '2', url, refspec,
cwd=cwd
)
get_logger().info(stdout)
def checkout(cwd):
get_logger().info("Checking out")
stdout = _call('git', 'checkout', "FETCH_HEAD", cwd=cwd)
get_logger().info(stdout)
def show(*args, cwd=None):
get_logger().info("Show")
return _call('git', 'show', *args, cwd=cwd)
def diff(*args, cwd=None):
get_logger().info("Diff")
patch = _call('git', 'diff', *args, cwd=cwd)
if not patch:
get_logger().warning("No changes found")
return
return patch
def reset_local_changes(cwd):
get_logger().info("Reset local changes")
_call('git', 'checkout', "--force", cwd=cwd)
def add_comment(url: urllib3.util.Url, refspec, message):
*_, patchset, changenum = refspec.rsplit("/")
message = "'" + message.replace("'", "'\"'\"'") + "'"
return _call(
"ssh",
"-p", str(url.port),
f"{url.auth}@{url.host}",
"gerrit", "review",
"--message", message,
# "--code-review", score,
f"{patchset},{changenum}",
)
def list_comments(url: urllib3.util.Url, refspec):
*_, patchset, _ = refspec.rsplit("/")
stdout = _call(
"ssh",
"-p", str(url.port),
f"{url.auth}@{url.host}",
"gerrit", "query",
"--comments",
"--current-patch-set", patchset,
"--format", "JSON",
)
change_set, *_ = stdout.splitlines()
return json.loads(change_set)["currentPatchSet"]["comments"]
def prepare_repo(url: urllib3.util.Url, project, refspec):
repo_url = (f"{url.scheme}://{url.auth}@{url.host}:{url.port}/{project}")
directory = pathlib.Path(mkdtemp())
clone(repo_url, directory),
fetch(repo_url, refspec, cwd=directory)
checkout(cwd=directory)
return directory
def adopt_to_gerrit_message(message):
lines = message.splitlines()
buf = []
for line in lines:
# remove markdown formatting
line = (line.replace("*", "")
.replace("``", "`")
.replace("<details>", "")
.replace("</details>", "")
.replace("<summary>", "")
.replace("</summary>", ""))
line = line.strip()
if line.startswith('#'):
buf.append("\n" +
line.replace('#', '').removesuffix(":").strip() +
":")
continue
elif line.startswith('-'):
buf.append(line.removeprefix('-').strip())
continue
else:
buf.append(line)
return "\n".join(buf).strip()
def add_suggestion(src_filename, context: str, start, end: int):
with (
NamedTemporaryFile("w", delete=False) as tmp,
open(src_filename, "r") as src
):
lines = src.readlines()
tmp.writelines(lines[:start - 1])
if context:
tmp.write(context)
tmp.writelines(lines[end:])
shutil.copy(tmp.name, src_filename)
os.remove(tmp.name)
def upload_patch(patch, path):
patch_server_endpoint = get_settings().get(
'gerrit.patch_server_endpoint')
patch_server_token = get_settings().get(
'gerrit.patch_server_token')
response = requests.post(
patch_server_endpoint,
json={
"content": patch,
"path": path,
},
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {patch_server_token}",
}
)
response.raise_for_status()
patch_server_endpoint = patch_server_endpoint.rstrip("/")
return patch_server_endpoint + "/" + path
class GerritProvider(GitProvider):
def __init__(self, key: str, incremental=False):
self.project, self.refspec = key.split(':')
assert self.project, "Project name is required"
assert self.refspec, "Refspec is required"
base_url = get_settings().get('gerrit.url')
assert base_url, "Gerrit URL is required"
user = get_settings().get('gerrit.user')
assert user, "Gerrit user is required"
parsed = urllib3.util.parse_url(base_url)
self.parsed_url = urllib3.util.parse_url(
f"{parsed.scheme}://{user}@{parsed.host}:{parsed.port}"
)
self.repo_path = prepare_repo(
self.parsed_url, self.project, self.refspec
)
self.repo = Repo(self.repo_path)
assert self.repo
self.pr_url = base_url
self.pr = PullRequestMimic(self.get_pr_title(), self.get_diff_files())
def get_pr_title(self):
"""
Substitutes the branch-name as the PR-mimic title.
"""
return self.repo.branches[0].name
def get_issue_comments(self):
comments = list_comments(self.parsed_url, self.refspec)
Comments = namedtuple('Comments', ['reversed'])
Comment = namedtuple('Comment', ['body'])
return Comments([Comment(c['message']) for c in reversed(comments)])
def get_pr_labels(self):
raise NotImplementedError(
'Getting labels is not implemented for the gerrit provider')
def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False):
raise NotImplementedError(
'Adding reactions is not implemented for the gerrit provider')
def remove_reaction(self, issue_comment_id: int, reaction_id: int):
raise NotImplementedError(
'Removing reactions is not implemented for the gerrit provider')
def get_commit_messages(self):
return [self.repo.head.commit.message]
def get_repo_settings(self):
try:
with open(self.repo_path / ".pr_agent.toml", 'rb') as f:
contents = f.read()
return contents
except OSError:
return b""
def get_diff_files(self) -> list[FilePatchInfo]:
diffs = self.repo.head.commit.diff(
self.repo.head.commit.parents[0], # previous commit
create_patch=True,
R=True
)
diff_files = []
for diff_item in diffs:
if diff_item.a_blob is not None:
original_file_content_str = (
diff_item.a_blob.data_stream.read().decode('utf-8')
)
else:
original_file_content_str = "" # empty file
if diff_item.b_blob is not None:
new_file_content_str = diff_item.b_blob.data_stream.read(). \
decode('utf-8')
else:
new_file_content_str = "" # empty file
edit_type = EDIT_TYPE.MODIFIED
if diff_item.new_file:
edit_type = EDIT_TYPE.ADDED
elif diff_item.deleted_file:
edit_type = EDIT_TYPE.DELETED
elif diff_item.renamed_file:
edit_type = EDIT_TYPE.RENAMED
diff_files.append(
FilePatchInfo(
original_file_content_str,
new_file_content_str,
diff_item.diff.decode('utf-8'),
diff_item.b_path,
edit_type=edit_type,
old_filename=None
if diff_item.a_path == diff_item.b_path
else diff_item.a_path
)
)
self.diff_files = diff_files
return diff_files
def get_files(self):
diff_index = self.repo.head.commit.diff(
self.repo.head.commit.parents[0], # previous commit
R=True
)
# Get the list of changed files
diff_files = [item.a_path for item in diff_index]
return diff_files
def get_languages(self):
"""
Calculate percentage of languages in repository. Used for hunk
prioritisation.
"""
# Get all files in repository
filepaths = [Path(item.path) for item in
self.repo.tree().traverse() if item.type == 'blob']
# Identify language by file extension and count
lang_count = Counter(
ext.lstrip('.') for filepath in filepaths for ext in
[filepath.suffix.lower()])
# Convert counts to percentages
total_files = len(filepaths)
lang_percentage = {lang: count / total_files * 100 for lang, count
in lang_count.items()}
return lang_percentage
def get_pr_description_full(self):
return self.repo.head.commit.message
def get_user_id(self):
return self.repo.head.commit.author.email
def is_supported(self, capability: str) -> bool:
if capability in [
# 'get_issue_comments',
'create_inline_comment',
'publish_inline_comments',
'get_labels',
'gfm_markdown'
]:
return False
return True
def split_suggestion(self, msg) -> tuple[str, str]:
is_code_context = False
description = []
context = []
for line in msg.splitlines():
if line.startswith('```suggestion'):
is_code_context = True
continue
if line.startswith('```'):
is_code_context = False
continue
if is_code_context:
context.append(line)
else:
description.append(
line.replace('*', '')
)
return (
'\n'.join(description),
'\n'.join(context) + '\n' if context else ''
)
def publish_code_suggestions(self, code_suggestions: list):
msg = []
for suggestion in code_suggestions:
description, code = self.split_suggestion(suggestion['body'])
add_suggestion(
pathlib.Path(self.repo_path) / suggestion["relevant_file"],
code,
suggestion["relevant_lines_start"],
suggestion["relevant_lines_end"],
)
patch = diff(cwd=self.repo_path)
patch_id = uuid.uuid4().hex[0:4]
path = "/".join(["codium-ai", self.refspec, patch_id])
full_path = upload_patch(patch, path)
reset_local_changes(self.repo_path)
msg.append(f'* {description}\n{full_path}')
if msg:
add_comment(self.parsed_url, self.refspec, "\n".join(msg))
return True
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
if not is_temporary:
msg = adopt_to_gerrit_message(pr_comment)
add_comment(self.parsed_url, self.refspec, msg)
def publish_description(self, pr_title: str, pr_body: str):
msg = adopt_to_gerrit_message(pr_body)
add_comment(self.parsed_url, self.refspec, pr_title + '\n' + msg)
def publish_inline_comments(self, comments: list[dict]):
raise NotImplementedError(
'Publishing inline comments is not implemented for the gerrit '
'provider')
def publish_inline_comment(self, body: str, relevant_file: str,
relevant_line_in_file: str):
raise NotImplementedError(
'Publishing inline comments is not implemented for the gerrit '
'provider')
def publish_labels(self, labels):
# Not applicable to the local git provider,
# but required by the interface
pass
def remove_initial_comment(self):
# remove repo, cloned in previous steps
# shutil.rmtree(self.repo_path)
pass
def remove_comment(self, comment):
pass
def get_pr_branch(self):
return self.repo.head

View File

@ -1,27 +1,28 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
# enum EDIT_TYPE (ADDED, DELETED, MODIFIED, RENAMED)
from typing import Optional
@dataclass
class FilePatchInfo:
base_file: str
head_file: str
patch: str
filename: str
tokens: int = -1
from pr_agent.config_loader import get_settings
from pr_agent.algo.types import FilePatchInfo
from pr_agent.log import get_logger
class GitProvider(ABC):
@abstractmethod
def is_supported(self, capability: str) -> bool:
pass
@abstractmethod
def get_diff_files(self) -> list[FilePatchInfo]:
pass
@abstractmethod
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
def publish_description(self, pr_title: str, pr_body: str):
pass
@abstractmethod
def remove_initial_comment(self):
def publish_code_suggestions(self, code_suggestions: list) -> bool:
pass
@abstractmethod
@ -37,46 +38,241 @@ class GitProvider(ABC):
pass
@abstractmethod
def get_pr_description(self):
def get_pr_description_full(self) -> str:
pass
def edit_comment(self, comment, body: str):
pass
def reply_to_comment_from_comment_id(self, comment_id: int, body: str):
pass
def get_pr_description(self, *, full: bool = True) -> str:
from pr_agent.config_loader import get_settings
from pr_agent.algo.utils import clip_tokens
max_tokens_description = get_settings().get("CONFIG.MAX_DESCRIPTION_TOKENS", None)
description = self.get_pr_description_full() if full else self.get_user_description()
if max_tokens_description:
return clip_tokens(description, max_tokens_description)
return description
def get_user_description(self) -> str:
if hasattr(self, 'user_description') and not (self.user_description is None):
return self.user_description
description = (self.get_pr_description_full() or "").strip()
description_lowercase = description.lower()
get_logger().debug(f"Existing description", description=description_lowercase)
# if the existing description wasn't generated by the pr-agent, just return it as-is
if not self._is_generated_by_pr_agent(description_lowercase):
get_logger().info(f"Existing description was not generated by the pr-agent")
return description
# if the existing description was generated by the pr-agent, but it doesn't contain a user description,
# return nothing (empty string) because it means there is no user description
user_description_header = "## **user description**"
if user_description_header not in description_lowercase:
get_logger().info(f"Existing description was generated by the pr-agent, but it doesn't contain a user description")
return ""
# otherwise, extract the original user description from the existing pr-agent description and return it
# user_description_start_position = description_lowercase.find(user_description_header) + len(user_description_header)
# return description[user_description_start_position:].split("\n", 1)[-1].strip()
# the 'user description' is in the beginning. extract and return it
possible_headers = self._possible_headers()
start_position = description_lowercase.find(user_description_header) + len(user_description_header)
end_position = len(description)
for header in possible_headers: # try to clip at the next header
if header != user_description_header and header in description_lowercase:
end_position = min(end_position, description_lowercase.find(header))
if end_position != len(description) and end_position > start_position:
original_user_description = description[start_position:end_position].strip()
if original_user_description.endswith("___"):
original_user_description = original_user_description[:-3].strip()
else:
original_user_description = description.split("___")[0].strip()
if original_user_description.lower().startswith(user_description_header):
original_user_description = original_user_description[len(user_description_header):].strip()
get_logger().info(f"Extracted user description from existing description",
description=original_user_description)
self.user_description = original_user_description
return original_user_description
def _possible_headers(self):
return ("## **user description**", "## **pr type**", "## **pr description**", "## **pr labels**", "## **type**", "## **description**",
"## **labels**", "### 🤖 generated by pr agent")
def _is_generated_by_pr_agent(self, description_lowercase: str) -> bool:
possible_headers = self._possible_headers()
return any(description_lowercase.startswith(header) for header in possible_headers)
@abstractmethod
def get_repo_settings(self):
pass
def get_pr_id(self):
return ""
def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str:
return ""
#### comments operations ####
@abstractmethod
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
pass
def publish_persistent_comment(self, pr_comment: str, initial_header: str, update_header: bool, name='review'):
self.publish_comment(pr_comment)
@abstractmethod
def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
pass
def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str,
absolute_position: int = None):
raise NotImplementedError("This git provider does not support creating inline comments yet")
@abstractmethod
def publish_inline_comments(self, comments: list[dict]):
pass
@abstractmethod
def remove_initial_comment(self):
pass
@abstractmethod
def remove_comment(self, comment):
pass
@abstractmethod
def get_issue_comments(self):
pass
def get_comment_url(self, comment) -> str:
return ""
#### labels operations ####
@abstractmethod
def publish_labels(self, labels):
pass
@abstractmethod
def get_pr_labels(self):
pass
def get_repo_labels(self):
pass
@abstractmethod
def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) -> Optional[int]:
pass
@abstractmethod
def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool:
pass
#### commits operations ####
@abstractmethod
def get_commit_messages(self):
pass
def get_pr_url(self) -> str:
if hasattr(self, 'pr_url'):
return self.pr_url
return ""
def get_latest_commit_url(self) -> str:
return ""
def auto_approve(self) -> bool:
return False
def calc_pr_statistics(self, pull_request_data: dict):
return {}
def get_main_pr_language(languages, files) -> str:
"""
Get the main language of the commit. Return an empty string if cannot determine.
"""
main_language_str = ""
if not languages:
get_logger().info("No languages detected")
return main_language_str
if not files:
get_logger().info("No files in diff")
return main_language_str
try:
top_language = max(languages, key=languages.get).lower()
# validate that the specific commit uses the main language
extension_list = []
for file in files:
if not file:
continue
if isinstance(file, str):
file = FilePatchInfo(base_file=None, head_file=None, patch=None, filename=file)
extension_list.append(file.filename.rsplit('.')[-1])
# get the most common extension
most_common_extension = max(set(extension_list), key=extension_list.count)
most_common_extension = '.' + max(set(extension_list), key=extension_list.count)
try:
language_extension_map_org = get_settings().language_extension_map_org
language_extension_map = {k.lower(): v for k, v in language_extension_map_org.items()}
# look for a match. TBD: add more languages, do this systematically
if most_common_extension == 'py' and top_language == 'python' or \
most_common_extension == 'js' and top_language == 'javascript' or \
most_common_extension == 'ts' and top_language == 'typescript' or \
most_common_extension == 'go' and top_language == 'go' or \
most_common_extension == 'java' and top_language == 'java' or \
most_common_extension == 'c' and top_language == 'c' or \
most_common_extension == 'cpp' and top_language == 'c++' or \
most_common_extension == 'cs' and top_language == 'c#' or \
most_common_extension == 'swift' and top_language == 'swift' or \
most_common_extension == 'php' and top_language == 'php' or \
most_common_extension == 'rb' and top_language == 'ruby' or \
most_common_extension == 'rs' and top_language == 'rust' or \
most_common_extension == 'scala' and top_language == 'scala' or \
most_common_extension == 'kt' and top_language == 'kotlin' or \
most_common_extension == 'pl' and top_language == 'perl' or \
most_common_extension == 'swift' and top_language == 'swift':
main_language_str = top_language
if top_language in language_extension_map and most_common_extension in language_extension_map[top_language]:
main_language_str = top_language
else:
for language, extensions in language_extension_map.items():
if most_common_extension in extensions:
main_language_str = language
break
except Exception as e:
get_logger().exception(f"Failed to get main language: {e}")
pass
except Exception:
## old approach:
# most_common_extension = max(set(extension_list), key=extension_list.count)
# if most_common_extension == 'py' and top_language == 'python' or \
# most_common_extension == 'js' and top_language == 'javascript' or \
# most_common_extension == 'ts' and top_language == 'typescript' or \
# most_common_extension == 'tsx' and top_language == 'typescript' or \
# most_common_extension == 'go' and top_language == 'go' or \
# most_common_extension == 'java' and top_language == 'java' or \
# most_common_extension == 'c' and top_language == 'c' or \
# most_common_extension == 'cpp' and top_language == 'c++' or \
# most_common_extension == 'cs' and top_language == 'c#' or \
# most_common_extension == 'swift' and top_language == 'swift' or \
# most_common_extension == 'php' and top_language == 'php' or \
# most_common_extension == 'rb' and top_language == 'ruby' or \
# most_common_extension == 'rs' and top_language == 'rust' or \
# most_common_extension == 'scala' and top_language == 'scala' or \
# most_common_extension == 'kt' and top_language == 'kotlin' or \
# most_common_extension == 'pl' and top_language == 'perl' or \
# most_common_extension == top_language:
# main_language_str = top_language
except Exception as e:
get_logger().exception(e)
pass
return main_language_str
class IncrementalPR:
def __init__(self, is_incremental: bool = False):
self.is_incremental = is_incremental
self.commits_range = None
self.first_new_commit = None
self.last_seen_commit = None
@property
def first_new_commit_sha(self):
return None if self.first_new_commit is None else self.first_new_commit.sha
@property
def last_seen_commit_sha(self):
return None if self.last_seen_commit is None else self.last_seen_commit.sha

View File

@ -1,43 +1,226 @@
import logging
import time
import hashlib
from datetime import datetime
from typing import Optional, Tuple
from urllib.parse import urlparse
from github import AppAuthentication, Github
from github import AppAuthentication, Auth, Github, GithubException
from retry import retry
from starlette_context import context
from pr_agent.config_loader import settings
from .git_provider import FilePatchInfo
from ..algo.language_handler import is_valid_file
from ..algo.utils import load_large_diff, clip_tokens, find_line_number_of_relevant_line_in_file
from ..config_loader import get_settings
from ..log import get_logger
from ..servers.utils import RateLimitExceeded
from .git_provider import GitProvider, IncrementalPR
from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
class GithubProvider:
def __init__(self, pr_url: Optional[str] = None):
self.installation_id = settings.get("GITHUB.INSTALLATION_ID")
class GithubProvider(GitProvider):
def __init__(self, pr_url: Optional[str] = None, incremental=IncrementalPR(False)):
self.repo_obj = None
try:
self.installation_id = context.get("installation_id", None)
except Exception:
self.installation_id = None
self.base_url = get_settings().get("GITHUB.BASE_URL", "https://api.github.com").rstrip("/")
self.base_url_html = self.base_url.split("api/")[0].rstrip("/") if "api/" in self.base_url else "https://github.com"
self.github_client = self._get_github_client()
self.repo = None
self.pr_num = None
self.pr = None
self.github_user_id = None
if pr_url:
self.diff_files = None
self.git_files = None
self.incremental = incremental
if pr_url and 'pull' in pr_url:
self.set_pr(pr_url)
self.pr_commits = list(self.pr.get_commits())
if self.incremental.is_incremental:
self.get_incremental_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:
self.pr_commits = None
def is_supported(self, capability: str) -> bool:
return True
def get_pr_url(self) -> str:
return self.pr.html_url
def set_pr(self, pr_url: str):
self.repo, self.pr_num = self._parse_pr_url(pr_url)
self.pr = self._get_pr()
def get_files(self):
return self.pr.get_files()
def get_incremental_commits(self):
if not self.pr_commits:
self.pr_commits = list(self.pr.get_commits())
self.previous_review = self.get_previous_review(full=True, incremental=True)
if self.previous_review:
self.incremental.commits_range = self.get_commit_range()
# Get all files changed during the commit range
self.file_set = dict()
for commit in self.incremental.commits_range:
if commit.commit.message.startswith(f"Merge branch '{self._get_repo().default_branch}'"):
get_logger().info(f"Skipping merge commit {commit.commit.message}")
continue
self.file_set.update({file.filename: file for file in commit.files})
else:
raise ValueError("No previous review found")
def get_commit_range(self):
last_review_time = self.previous_review.created_at
first_new_commit_index = None
for index in range(len(self.pr_commits) - 1, -1, -1):
if self.pr_commits[index].commit.author.date > last_review_time:
self.incremental.first_new_commit = self.pr_commits[index]
first_new_commit_index = index
else:
self.incremental.last_seen_commit = self.pr_commits[index]
break
return self.pr_commits[first_new_commit_index:] if first_new_commit_index is not None else []
def get_previous_review(self, *, full: bool, incremental: bool):
if not (full or incremental):
raise ValueError("At least one of full or incremental must be True")
if not getattr(self, "comments", None):
self.comments = list(self.pr.get_issue_comments())
prefixes = []
if full:
prefixes.append("## PR Review")
if incremental:
prefixes.append("## Incremental PR Review")
for index in range(len(self.comments) - 1, -1, -1):
if any(self.comments[index].body.startswith(prefix) for prefix in prefixes):
return self.comments[index]
def get_files(self):
if self.incremental.is_incremental and self.file_set:
return self.file_set.values()
try:
git_files = context.get("git_files", None)
if git_files:
return git_files
self.git_files = self.pr.get_files()
context["git_files"] = self.git_files
return self.git_files
except Exception:
if not self.git_files:
self.git_files = self.pr.get_files()
return self.git_files
@retry(exceptions=RateLimitExceeded,
tries=get_settings().github.ratelimit_retries, delay=2, backoff=2, jitter=(1, 3))
def get_diff_files(self) -> list[FilePatchInfo]:
files = self.pr.get_files()
diff_files = []
for file in files:
original_file_content_str = self._get_pr_file_content(file, self.pr.base.sha)
new_file_content_str = self._get_pr_file_content(file, self.pr.head.sha)
diff_files.append(FilePatchInfo(original_file_content_str, new_file_content_str, file.patch, file.filename))
return diff_files
"""
Retrieves the list of files that have been modified, added, deleted, or renamed in a pull request in GitHub,
along with their content and patch information.
Returns:
diff_files (List[FilePatchInfo]): List of FilePatchInfo objects representing the modified, added, deleted,
or renamed files in the merge request.
"""
try:
try:
diff_files = context.get("diff_files", None)
if diff_files:
return diff_files
except Exception:
pass
if self.diff_files:
return self.diff_files
files = self.get_files()
diff_files = []
for file in files:
if not is_valid_file(file.filename):
continue
new_file_content_str = self._get_pr_file_content(file, self.pr.head.sha) # communication with GitHub
patch = file.patch
if self.incremental.is_incremental and self.file_set:
original_file_content_str = self._get_pr_file_content(file, self.incremental.last_seen_commit_sha)
patch = load_large_diff(file.filename, new_file_content_str, original_file_content_str)
self.file_set[file.filename] = patch
else:
original_file_content_str = self._get_pr_file_content(file, self.pr.base.sha)
if not patch:
patch = load_large_diff(file.filename, new_file_content_str, original_file_content_str)
if file.status == 'added':
edit_type = EDIT_TYPE.ADDED
elif file.status == 'removed':
edit_type = EDIT_TYPE.DELETED
elif file.status == 'renamed':
edit_type = EDIT_TYPE.RENAMED
elif file.status == 'modified':
edit_type = EDIT_TYPE.MODIFIED
else:
get_logger().error(f"Unknown edit type: {file.status}")
edit_type = EDIT_TYPE.UNKNOWN
# count number of lines added and removed
patch_lines = patch.splitlines(keepends=True)
num_plus_lines = len([line for line in patch_lines if line.startswith('+')])
num_minus_lines = len([line for line in patch_lines if line.startswith('-')])
file_patch_canonical_structure = FilePatchInfo(original_file_content_str, new_file_content_str, patch,
file.filename, edit_type=edit_type,
num_plus_lines=num_plus_lines,
num_minus_lines=num_minus_lines,)
diff_files.append(file_patch_canonical_structure)
self.diff_files = diff_files
try:
context["diff_files"] = diff_files
except Exception:
pass
return diff_files
except GithubException.RateLimitExceededException as e:
get_logger().error(f"Rate limit exceeded for GitHub API. Original message: {e}")
raise RateLimitExceeded("Rate limit exceeded for GitHub API.") from e
def publish_description(self, pr_title: str, pr_body: str):
self.pr.edit(title=pr_title, body=pr_body)
def get_latest_commit_url(self) -> str:
return self.last_commit_id.html_url
def get_comment_url(self, comment) -> str:
return comment.html_url
def publish_persistent_comment(self, pr_comment: str, initial_header: str, update_header: bool = True, name='review'):
prev_comments = list(self.pr.get_issue_comments())
for comment in prev_comments:
body = comment.body
if body.startswith(initial_header):
latest_commit_url = self.get_latest_commit_url()
comment_url = self.get_comment_url(comment)
if update_header:
updated_header = f"{initial_header}\n\n### ({name.capitalize()} updated until commit {latest_commit_url})\n"
pr_comment_updated = pr_comment.replace(initial_header, updated_header)
else:
pr_comment_updated = pr_comment
get_logger().info(f"Persistent mode- updating comment {comment_url} to latest review message")
response = comment.edit(pr_comment_updated)
self.publish_comment(
f"**[Persistent {name}]({comment_url})** updated to latest commit {latest_commit_url}")
return
self.publish_comment(pr_comment)
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
if is_temporary and not get_settings().config.publish_output_progress:
get_logger().debug(f"Skipping publish_comment for temporary comment: {pr_comment}")
return
response = self.pr.create_issue_comment(pr_comment)
if hasattr(response, "user") and hasattr(response.user, "login"):
self.github_user_id = response.user.login
@ -45,21 +228,215 @@ class GithubProvider:
if not hasattr(self.pr, 'comments_list'):
self.pr.comments_list = []
self.pr.comments_list.append(response)
return response
def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
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.diff_files,
relevant_file.strip('`'),
relevant_line_in_file,
absolute_position)
if position == -1:
if get_settings().config.verbosity_level >= 2:
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()
return dict(body=body, path=path, position=position) if subject_type == "LINE" else {}
def publish_inline_comments(self, comments: list[dict], disable_fallback: bool = False):
try:
# publish all comments in a single message
self.pr.create_review(commit=self.last_commit_id, comments=comments)
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to publish inline comments")
if (getattr(e, "status", None) == 422
and get_settings().github.publish_inline_comments_fallback_with_verification and not disable_fallback):
pass # continue to try _publish_inline_comments_fallback_with_verification
else:
raise e # will end up with publishing the comments one by one
try:
self._publish_inline_comments_fallback_with_verification(comments)
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to publish inline code comments fallback, error: {e}")
raise e
def _publish_inline_comments_fallback_with_verification(self, comments: list[dict]):
"""
Check each inline comment separately against the GitHub API and discard of invalid comments,
then publish all the remaining valid comments in a single review.
For invalid comments, also try removing the suggestion part and posting the comment just on the first line.
"""
verified_comments, invalid_comments = self._verify_code_comments(comments)
# publish as a group the verified comments
if verified_comments:
try:
self.pr.create_review(commit=self.last_commit_id, comments=verified_comments)
except:
pass
# try to publish one by one the invalid comments as a one-line code comment
if invalid_comments and get_settings().github.try_fix_invalid_inline_comments:
fixed_comments_as_one_liner = self._try_fix_invalid_inline_comments(
[comment for comment, _ in invalid_comments])
for comment in fixed_comments_as_one_liner:
try:
self.publish_inline_comments([comment], disable_fallback=True)
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"Published invalid comment as a single line comment: {comment}")
except:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to publish invalid comment as a single line comment: {comment}")
def _verify_code_comment(self, comment: dict):
is_verified = False
e = None
try:
# event ="" # By leaving this blank, you set the review action state to PENDING
input = dict(commit_id=self.last_commit_id.sha, comments=[comment])
headers, data = self.pr._requester.requestJsonAndCheck(
"POST", f"{self.pr.url}/reviews", input=input)
pending_review_id = data["id"]
is_verified = True
except Exception as err:
is_verified = False
pending_review_id = None
e = err
if pending_review_id is not None:
try:
self.pr._requester.requestJsonAndCheck("DELETE", f"{self.pr.url}/reviews/{pending_review_id}")
except Exception:
pass
return is_verified, e
def _verify_code_comments(self, comments: list[dict]) -> tuple[list[dict], list[tuple[dict, Exception]]]:
"""Very each comment against the GitHub API and return 2 lists: 1 of verified and 1 of invalid comments"""
verified_comments = []
invalid_comments = []
for comment in comments:
time.sleep(1) # for avoiding secondary rate limit
is_verified, e = self._verify_code_comment(comment)
if is_verified:
verified_comments.append(comment)
else:
invalid_comments.append((comment, e))
return verified_comments, invalid_comments
def _try_fix_invalid_inline_comments(self, invalid_comments: list[dict]) -> list[dict]:
"""
Try fixing invalid comments by removing the suggestion part and setting the comment just on the first line.
Return only comments that have been modified in some way.
This is a best-effort attempt to fix invalid comments, and should be verified accordingly.
"""
import copy
fixed_comments = []
for comment in invalid_comments:
try:
fixed_comment = copy.deepcopy(comment) # avoid modifying the original comment dict for later logging
if "```suggestion" in comment["body"]:
fixed_comment["body"] = comment["body"].split("```suggestion")[0]
if "start_line" in comment:
fixed_comment["line"] = comment["start_line"]
del fixed_comment["start_line"]
if "start_side" in comment:
fixed_comment["side"] = comment["start_side"]
del fixed_comment["start_side"]
if fixed_comment != comment:
fixed_comments.append(fixed_comment)
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to fix inline comment, error: {e}")
return fixed_comments
def publish_code_suggestions(self, code_suggestions: list) -> bool:
"""
Publishes code suggestions as comments on the PR.
"""
post_parameters_list = []
for suggestion in code_suggestions:
body = suggestion['body']
relevant_file = suggestion['relevant_file']
relevant_lines_start = suggestion['relevant_lines_start']
relevant_lines_end = suggestion['relevant_lines_end']
if not relevant_lines_start or relevant_lines_start == -1:
if get_settings().config.verbosity_level >= 2:
get_logger().exception(
f"Failed to publish code suggestion, relevant_lines_start is {relevant_lines_start}")
continue
if relevant_lines_end < relevant_lines_start:
if get_settings().config.verbosity_level >= 2:
get_logger().exception(f"Failed to publish code suggestion, "
f"relevant_lines_end is {relevant_lines_end} and "
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)
try:
self.publish_inline_comments(post_parameters_list)
return True
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to publish code suggestion, error: {e}")
return False
def edit_comment(self, comment, body: str):
comment.edit(body=body)
def reply_to_comment_from_comment_id(self, comment_id: int, body: str):
try:
# self.pr.get_issue_comment(comment_id).edit(body)
headers, data_patch = self.pr._requester.requestJsonAndCheck(
"POST", f"{self.base_url}/repos/{self.repo}/pulls/{self.pr_num}/comments/{comment_id}/replies",
input={"body": body}
)
except Exception as e:
get_logger().exception(f"Failed to reply comment, error: {e}")
def remove_initial_comment(self):
try:
for comment in self.pr.comments_list:
for comment in getattr(self.pr, 'comments_list', []):
if comment.is_temporary:
comment.delete()
self.remove_comment(comment)
except Exception as e:
logging.exception(f"Failed to remove initial comment, error: {e}")
get_logger().exception(f"Failed to remove initial comment, error: {e}")
def remove_comment(self, comment):
try:
comment.delete()
except Exception as e:
get_logger().exception(f"Failed to remove comment, error: {e}")
def get_title(self):
return self.pr.title
def get_description(self):
return self.pr.body
def get_languages(self):
languages = self._get_repo().get_languages()
return languages
@ -67,19 +444,20 @@ class GithubProvider:
def get_pr_branch(self):
return self.pr.head.ref
def get_pr_description(self):
def get_pr_description_full(self):
return self.pr.body
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):
deployment_type = settings.get("GITHUB.DEPLOYMENT_TYPE", "user")
deployment_type = get_settings().get("GITHUB.DEPLOYMENT_TYPE", "user")
if deployment_type != 'user':
raise ValueError("Deployment mode must be set to 'user' to get notifications")
@ -87,6 +465,44 @@ class GithubProvider:
notifications = self.github_client.get_user().get_notifications(since=since)
return notifications
def get_issue_comments(self):
return self.pr.get_issue_comments()
def get_repo_settings(self):
try:
# contents = self.repo_obj.get_contents(".pr_agent.toml", ref=self.pr.head.sha).decoded_content
# more logical to take 'pr_agent.toml' from the default branch
contents = self.repo_obj.get_contents(".pr_agent.toml").decoded_content
return contents
except Exception:
return ""
def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) -> Optional[int]:
if disable_eyes:
return None
try:
headers, data_patch = self.pr._requester.requestJsonAndCheck(
"POST", f"{self.base_url}/repos/{self.repo}/issues/comments/{issue_comment_id}/reactions",
input={"content": "eyes"}
)
return data_patch.get("id", None)
except Exception as e:
get_logger().exception(f"Failed to add eyes reaction, error: {e}")
return None
def remove_reaction(self, issue_comment_id: int, reaction_id: str) -> bool:
try:
# self.pr.get_issue_comment(issue_comment_id).delete_reaction(reaction_id)
headers, data_patch = self.pr._requester.requestJsonAndCheck(
"DELETE",
f"{self.base_url}/repos/{self.repo}/issues/comments/{issue_comment_id}/reactions/{reaction_id}"
)
return True
except Exception as e:
get_logger().exception(f"Failed to remove eyes reaction, error: {e}")
return False
@staticmethod
def _parse_pr_url(pr_url: str) -> Tuple[str, int]:
parsed_url = urlparse(pr_url)
@ -116,39 +532,193 @@ class GithubProvider:
return repo_name, pr_number
@staticmethod
def _parse_issue_url(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")
path_parts = parsed_url.path.strip('/').split('/')
if 'api.github.com' in parsed_url.netloc:
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])
try:
issue_number = int(path_parts[4])
except ValueError as e:
raise ValueError("Unable to convert issue number to integer") from e
return repo_name, issue_number
if len(path_parts) < 4 or path_parts[2] != 'issues':
raise ValueError("The provided URL does not appear to be a GitHub PR issue")
repo_name = '/'.join(path_parts[:2])
try:
issue_number = int(path_parts[3])
except ValueError as e:
raise ValueError("Unable to convert issue number to integer") from e
return repo_name, issue_number
def _get_github_client(self):
deployment_type = settings.get("GITHUB.DEPLOYMENT_TYPE", "user")
deployment_type = get_settings().get("GITHUB.DEPLOYMENT_TYPE", "user")
if deployment_type == 'app':
try:
private_key = settings.github.private_key
app_id = settings.github.app_id
private_key = get_settings().github.private_key
app_id = get_settings().github.app_id
except AttributeError as e:
raise ValueError("GitHub app ID and private key are required when using GitHub app deployment") from e
if not self.installation_id:
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)
return Github(app_auth=auth, base_url=self.base_url)
if deployment_type == 'user':
try:
token = settings.github.user_token
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(token)
return Github(auth=Auth.Token(token), base_url=self.base_url)
def _get_repo(self):
return self.github_client.get_repo(self.repo)
if hasattr(self, 'repo_obj') and \
hasattr(self.repo_obj, 'full_name') and \
self.repo_obj.full_name == self.repo:
return self.repo_obj
else:
self.repo_obj = self.github_client.get_repo(self.repo)
return self.repo_obj
def _get_pr(self):
return self._get_repo().get_pull(self.pr_num)
def _get_pr_file_content(self, file: FilePatchInfo, sha: str):
def _get_pr_file_content(self, file: FilePatchInfo, sha: str) -> str:
try:
file_content_str = self._get_repo().get_contents(file.filename, ref=sha).decoded_content.decode()
file_content_str = str(self._get_repo().get_contents(file.filename, ref=sha).decoded_content.decode())
except Exception:
file_content_str = ""
return file_content_str
def publish_labels(self, pr_types):
try:
label_color_map = {"Bug fix": "1d76db", "Tests": "e99695", "Bug fix with tests": "c5def5",
"Enhancement": "bfd4f2", "Documentation": "d4c5f9",
"Other": "d1bcf9"}
post_parameters = []
for p in pr_types:
color = label_color_map.get(p, "d1bcf9") # default to "Other" color
post_parameters.append({"name": p, "color": color})
headers, data = self.pr._requester.requestJsonAndCheck(
"PUT", f"{self.pr.issue_url}/labels", input=post_parameters
)
except Exception as e:
get_logger().exception(f"Failed to publish labels, error: {e}")
def get_pr_labels(self):
try:
return [label.name for label in self.pr.labels]
except Exception as e:
get_logger().exception(f"Failed to get labels, error: {e}")
return []
def get_repo_labels(self):
labels = self.repo_obj.get_labels()
return [label for label in labels]
def get_commit_messages(self):
"""
Retrieves the commit messages of a pull request.
Returns:
str: A string containing the commit messages of the pull request.
"""
max_tokens = get_settings().get("CONFIG.MAX_COMMITS_TOKENS", None)
try:
commit_list = self.pr.get_commits()
commit_messages = [commit.commit.message for commit in commit_list]
commit_messages_str = "\n".join([f"{i + 1}. {message}" for i, message in enumerate(commit_messages)])
except Exception:
commit_messages_str = ""
if max_tokens:
commit_messages_str = clip_tokens(commit_messages_str, max_tokens)
return commit_messages_str
def generate_link_to_relevant_line_number(self, suggestion) -> str:
try:
relevant_file = suggestion['relevant_file'].strip('`').strip("'").strip('\n')
relevant_line_str = suggestion['relevant_line'].strip('\n')
if not relevant_line_str:
return ""
position, absolute_position = find_line_number_of_relevant_line_in_file \
(self.diff_files, relevant_file, relevant_line_str)
if absolute_position != -1:
# # link to right file only
# link = f"https://github.com/{self.repo}/blob/{self.pr.head.sha}/{relevant_file}" \
# + "#" + f"L{absolute_position}"
# link to diff
sha_file = hashlib.sha256(relevant_file.encode('utf-8')).hexdigest()
link = f"{self.base_url_html}/{self.repo}/pull/{self.pr_num}/files#diff-{sha_file}R{absolute_position}"
return link
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"Failed adding line link, error: {e}")
return ""
def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str:
sha_file = hashlib.sha256(relevant_file.encode('utf-8')).hexdigest()
if relevant_line_start == -1:
link = f"{self.base_url_html}/{self.repo}/pull/{self.pr_num}/files#diff-{sha_file}"
elif relevant_line_end:
link = f"{self.base_url_html}/{self.repo}/pull/{self.pr_num}/files#diff-{sha_file}R{relevant_line_start}-R{relevant_line_end}"
else:
link = f"{self.base_url_html}/{self.repo}/pull/{self.pr_num}/files#diff-{sha_file}R{relevant_line_start}"
return link
def get_pr_id(self):
try:
pr_id = f"{self.repo}/{self.pr_num}"
return pr_id
except:
return ""
def auto_approve(self) -> bool:
try:
res = self.pr.create_review(event="APPROVE")
if res.state == "APPROVED":
return True
return False
except Exception as e:
get_logger().exception(f"Failed to auto-approve, error: {e}")
return False
def calc_pr_statistics(self, pull_request_data: dict):
try:
out = {}
from datetime import datetime
created_at = pull_request_data['created_at']
closed_at = pull_request_data['closed_at']
closed_at_datetime = datetime.strptime(closed_at, "%Y-%m-%dT%H:%M:%SZ")
created_at_datetime = datetime.strptime(created_at, "%Y-%m-%dT%H:%M:%SZ")
difference = closed_at_datetime - created_at_datetime
out['hours'] = difference.total_seconds() / 3600
out['commits'] = pull_request_data['commits']
out['comments'] = pull_request_data['comments']
out['review_comments'] = pull_request_data['review_comments']
out['changed_files'] = pull_request_data['changed_files']
out['additions'] = pull_request_data['additions']
out['deletions'] = pull_request_data['deletions']
except Exception as e:
get_logger().exception(f"Failed to calculate PR statistics, error: {e}")
return {}
return out

View File

@ -1,31 +1,52 @@
import logging
import hashlib
import re
from typing import Optional, Tuple
from urllib.parse import urlparse
import gitlab
from gitlab import GitlabGetError
from pr_agent.config_loader import settings
from ..algo.language_handler import is_valid_file
from ..algo.utils import load_large_diff, clip_tokens, find_line_number_of_relevant_line_in_file
from ..config_loader import get_settings
from .git_provider import GitProvider
from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
from ..log import get_logger
from .git_provider import FilePatchInfo, GitProvider
class DiffNotFoundError(Exception):
"""Raised when the diff for a merge request cannot be found."""
pass
class GitLabProvider(GitProvider):
def __init__(self, merge_request_url: Optional[str] = None):
gitlab_url = settings.get("GITLAB.URL", None)
def __init__(self, merge_request_url: Optional[str] = None, incremental: Optional[bool] = False):
gitlab_url = get_settings().get("GITLAB.URL", None)
if not gitlab_url:
raise ValueError("GitLab URL is not set in the config file")
gitlab_access_token = settings.get("GITLAB.PERSONAL_ACCESS_TOKEN", None)
gitlab_access_token = get_settings().get("GITLAB.PERSONAL_ACCESS_TOKEN", None)
if not gitlab_access_token:
raise ValueError("GitLab personal access token is not set in the config file")
self.gl = gitlab.Gitlab(
gitlab_url,
gitlab_access_token
url=gitlab_url,
oauth_token=gitlab_access_token
)
self.id_project = None
self.id_mr = None
self.mr = None
self.diff_files = None
self.git_files = None
self.temp_comments = []
self.pr_url = merge_request_url
self._set_merge_request(merge_request_url)
self.RE_HUNK_HEADER = re.compile(
r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)")
self.incremental = incremental
def is_supported(self, capability: str) -> bool:
if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments']: # gfm_markdown is supported in gitlab !
return False
return True
@property
def pr(self):
@ -35,33 +56,298 @@ class GitLabProvider(GitProvider):
def _set_merge_request(self, merge_request_url: str):
self.id_project, self.id_mr = self._parse_merge_request_url(merge_request_url)
self.mr = self._get_merge_request()
try:
self.last_diff = self.mr.diffs.list(get_all=True)[-1]
except IndexError as e:
get_logger().error(f"Could not get diff for merge request {self.id_mr}")
raise DiffNotFoundError(f"Could not get diff for merge request {self.id_mr}") from e
def _get_pr_file_content(self, file_path: str, branch: str) -> str:
try:
return self.gl.projects.get(self.id_project).files.get(file_path, branch).decode()
except GitlabGetError:
# In case of file creation the method returns GitlabGetError (404 file not found).
# In this case we return an empty string for the diff.
return ''
def get_diff_files(self) -> list[FilePatchInfo]:
"""
Retrieves the list of files that have been modified, added, deleted, or renamed in a pull request in GitLab,
along with their content and patch information.
Returns:
diff_files (List[FilePatchInfo]): List of FilePatchInfo objects representing the modified, added, deleted,
or renamed files in the merge request.
"""
if self.diff_files:
return self.diff_files
diffs = self.mr.changes()['changes']
diff_files = [FilePatchInfo("", "", diff['diff'], diff['new_path']) for diff in diffs]
diff_files = []
for diff in diffs:
if is_valid_file(diff['new_path']):
# original_file_content_str = self._get_pr_file_content(diff['old_path'], self.mr.target_branch)
# new_file_content_str = self._get_pr_file_content(diff['new_path'], self.mr.source_branch)
original_file_content_str = self._get_pr_file_content(diff['old_path'], self.mr.diff_refs['base_sha'])
new_file_content_str = self._get_pr_file_content(diff['new_path'], self.mr.diff_refs['head_sha'])
try:
if isinstance(original_file_content_str, bytes):
original_file_content_str = bytes.decode(original_file_content_str, 'utf-8')
if isinstance(new_file_content_str, bytes):
new_file_content_str = bytes.decode(new_file_content_str, 'utf-8')
except UnicodeDecodeError:
get_logger().warning(
f"Cannot decode file {diff['old_path']} or {diff['new_path']} in merge request {self.id_mr}")
edit_type = EDIT_TYPE.MODIFIED
if diff['new_file']:
edit_type = EDIT_TYPE.ADDED
elif diff['deleted_file']:
edit_type = EDIT_TYPE.DELETED
elif diff['renamed_file']:
edit_type = EDIT_TYPE.RENAMED
filename = diff['new_path']
patch = diff['diff']
if not patch:
patch = load_large_diff(filename, new_file_content_str, original_file_content_str)
# count number of lines added and removed
patch_lines = patch.splitlines(keepends=True)
num_plus_lines = len([line for line in patch_lines if line.startswith('+')])
num_minus_lines = len([line for line in patch_lines if line.startswith('-')])
diff_files.append(
FilePatchInfo(original_file_content_str, new_file_content_str,
patch=patch,
filename=filename,
edit_type=edit_type,
old_filename=None if diff['old_path'] == diff['new_path'] else diff['old_path'],
num_plus_lines=num_plus_lines,
num_minus_lines=num_minus_lines, ))
self.diff_files = diff_files
return diff_files
def get_files(self):
return [change['new_path'] for change in self.mr.changes()['changes']]
if not self.git_files:
self.git_files = [change['new_path'] for change in self.mr.changes()['changes']]
return self.git_files
def publish_description(self, pr_title: str, pr_body: str):
try:
self.mr.title = pr_title
self.mr.description = pr_body
self.mr.save()
except Exception as e:
get_logger().exception(f"Could not update merge request {self.id_mr} description: {e}")
def get_latest_commit_url(self):
return self.mr.commits().next().web_url
def get_comment_url(self, comment):
return f"{self.mr.web_url}#note_{comment.id}"
def publish_persistent_comment(self, pr_comment: str, initial_header: str, update_header: bool = True, name='review'):
try:
for comment in self.mr.notes.list(get_all=True)[::-1]:
if comment.body.startswith(initial_header):
latest_commit_url = self.get_latest_commit_url()
comment_url = self.get_comment_url(comment)
if update_header:
updated_header = f"{initial_header}\n\n### ({name.capitalize()} updated until commit {latest_commit_url})\n"
pr_comment_updated = pr_comment.replace(initial_header, updated_header)
else:
pr_comment_updated = pr_comment
get_logger().info(f"Persistent mode - updating comment {comment_url} to latest {name} message")
response = self.mr.notes.update(comment.id, {'body': pr_comment_updated})
self.publish_comment(
f"**[Persistent {name}]({comment_url})** updated to latest commit {latest_commit_url}")
return
except Exception as e:
get_logger().exception(f"Failed to update persistent review, error: {e}")
pass
self.publish_comment(pr_comment)
def publish_comment(self, mr_comment: str, is_temporary: bool = False):
comment = self.mr.notes.create({'body': mr_comment})
if is_temporary:
self.temp_comments.append(comment)
return comment
def edit_comment(self, comment, body: str):
self.mr.notes.update(comment.id,{'body': body} )
def reply_to_comment_from_comment_id(self, comment_id: int, body: str):
discussion = self.mr.discussions.get(comment_id)
discussion.notes.create({'body': body})
def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
edit_type, found, source_line_no, target_file, target_line_no = self.search_line(relevant_file,
relevant_line_in_file)
self.send_inline_comment(body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no,
target_file, target_line_no)
def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str, absolute_position: int = None):
raise NotImplementedError("Gitlab provider does not support creating inline comments yet")
def create_inline_comments(self, comments: list[dict]):
raise NotImplementedError("Gitlab provider does not support publishing inline comments yet")
def send_inline_comment(self,body: str,edit_type: str,found: bool,relevant_file: str,relevant_line_in_file: int,
source_line_no: int, target_file: str,target_line_no: int) -> None:
if not found:
get_logger().info(f"Could not find position for {relevant_file} {relevant_line_in_file}")
else:
# in order to have exact sha's we have to find correct diff for this change
diff = self.get_relevant_diff(relevant_file, relevant_line_in_file)
if diff is None:
get_logger().error(f"Could not get diff for merge request {self.id_mr}")
raise DiffNotFoundError(f"Could not get diff for merge request {self.id_mr}")
pos_obj = {'position_type': 'text',
'new_path': target_file.filename,
'old_path': target_file.old_filename if target_file.old_filename else target_file.filename,
'base_sha': diff.base_commit_sha, 'start_sha': diff.start_commit_sha, 'head_sha': diff.head_commit_sha}
if edit_type == 'deletion':
pos_obj['old_line'] = source_line_no - 1
elif edit_type == 'addition':
pos_obj['new_line'] = target_line_no - 1
else:
pos_obj['new_line'] = target_line_no - 1
pos_obj['old_line'] = source_line_no - 1
get_logger().debug(f"Creating comment in {self.id_mr} with body {body} and position {pos_obj}")
try:
self.mr.discussions.create({'body': body, 'position': pos_obj})
except Exception as e:
get_logger().debug(
f"Failed to create comment in {self.id_mr} with position {pos_obj} (probably not a '+' line)")
def get_relevant_diff(self, relevant_file: str, relevant_line_in_file: int) -> Optional[dict]:
changes = self.mr.changes() # Retrieve the changes for the merge request once
if not changes:
get_logger().error('No changes found for the merge request.')
return None
all_diffs = self.mr.diffs.list(get_all=True)
if not all_diffs:
get_logger().error('No diffs found for the merge request.')
return None
for diff in all_diffs:
for change in changes['changes']:
if change['new_path'] == relevant_file and relevant_line_in_file in change['diff']:
return diff
get_logger().debug(
f'No relevant diff found for {relevant_file} {relevant_line_in_file}. Falling back to last diff.')
return self.last_diff # fallback to last_diff if no relevant diff is found
def publish_code_suggestions(self, code_suggestions: list) -> bool:
for suggestion in code_suggestions:
try:
body = suggestion['body']
relevant_file = suggestion['relevant_file']
relevant_lines_start = suggestion['relevant_lines_start']
relevant_lines_end = suggestion['relevant_lines_end']
diff_files = self.get_diff_files()
target_file = None
for file in diff_files:
if file.filename == relevant_file:
if file.filename == relevant_file:
target_file = file
break
range = relevant_lines_end - relevant_lines_start # no need to add 1
body = body.replace('```suggestion', f'```suggestion:-0+{range}')
lines = target_file.head_file.splitlines()
relevant_line_in_file = lines[relevant_lines_start - 1]
# edit_type, found, source_line_no, target_file, target_line_no = self.find_in_file(target_file,
# relevant_line_in_file)
# for code suggestions, we want to edit the new code
source_line_no = None
target_line_no = relevant_lines_start + 1
found = True
edit_type = 'addition'
self.send_inline_comment(body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no,
target_file, target_line_no)
except Exception as e:
get_logger().exception(f"Could not publish code suggestion:\nsuggestion: {suggestion}\nerror: {e}")
# note that we publish suggestions one-by-one. so, if one fails, the rest will still be published
return True
def search_line(self, relevant_file, relevant_line_in_file):
target_file = None
edit_type = self.get_edit_type(relevant_line_in_file)
for file in self.get_diff_files():
if file.filename == relevant_file:
edit_type, found, source_line_no, target_file, target_line_no = self.find_in_file(file,
relevant_line_in_file)
return edit_type, found, source_line_no, target_file, target_line_no
def find_in_file(self, file, relevant_line_in_file):
edit_type = 'context'
source_line_no = 0
target_line_no = 0
found = False
target_file = file
patch = file.patch
patch_lines = patch.splitlines()
for line in patch_lines:
if line.startswith('@@'):
match = self.RE_HUNK_HEADER.match(line)
if not match:
continue
start_old, size_old, start_new, size_new, _ = match.groups()
source_line_no = int(start_old)
target_line_no = int(start_new)
continue
if line.startswith('-'):
source_line_no += 1
elif line.startswith('+'):
target_line_no += 1
elif line.startswith(' '):
source_line_no += 1
target_line_no += 1
if relevant_line_in_file in line:
found = True
edit_type = self.get_edit_type(line)
break
elif relevant_line_in_file[0] == '+' and relevant_line_in_file[1:].lstrip() in line:
# The model often adds a '+' to the beginning of the relevant_line_in_file even if originally
# it's a context line
found = True
edit_type = self.get_edit_type(line)
break
return edit_type, found, source_line_no, target_file, target_line_no
def get_edit_type(self, relevant_line_in_file):
edit_type = 'context'
if relevant_line_in_file[0] == '-':
edit_type = 'deletion'
elif relevant_line_in_file[0] == '+':
edit_type = 'addition'
return edit_type
def remove_initial_comment(self):
try:
for comment in self.temp_comments:
comment.delete()
self.remove_comment(comment)
except Exception as e:
logging.exception(f"Failed to remove temp comments, error: {e}")
get_logger().exception(f"Failed to remove temp comments, error: {e}")
def remove_comment(self, comment):
try:
comment.delete()
except Exception as e:
get_logger().exception(f"Failed to remove comment, error: {e}")
def get_title(self):
return self.mr.title
def get_description(self):
return self.mr.description
def get_languages(self):
languages = self.gl.projects.get(self.id_project).languages()
return languages
@ -69,24 +355,127 @@ class GitLabProvider(GitProvider):
def get_pr_branch(self):
return self.mr.source_branch
def get_pr_description(self):
def get_pr_description_full(self):
return self.mr.description
def _parse_merge_request_url(self, merge_request_url: str) -> Tuple[int, int]:
def get_issue_comments(self):
raise NotImplementedError("GitLab provider does not support issue comments yet")
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()
return contents
except Exception:
return ""
def add_eyes_reaction(self, issue_comment_id: int, disable_eyes: bool = False) -> Optional[int]:
return True
def remove_reaction(self, issue_comment_id: int, reaction_id: int) -> bool:
return True
def _parse_merge_request_url(self, merge_request_url: str) -> Tuple[str, int]:
parsed_url = urlparse(merge_request_url)
path_parts = parsed_url.path.strip('/').split('/')
if path_parts[-2] != 'merge_requests':
if 'merge_requests' not in path_parts:
raise ValueError("The provided URL does not appear to be a GitLab merge request URL")
mr_index = path_parts.index('merge_requests')
# Ensure there is an ID after 'merge_requests'
if len(path_parts) <= mr_index + 1:
raise ValueError("The provided URL does not contain a merge request ID")
try:
mr_id = int(path_parts[-1])
mr_id = int(path_parts[mr_index + 1])
except ValueError as e:
raise ValueError("Unable to convert merge request ID to integer") from e
# Gitlab supports access by both project numeric ID as well as 'namespace/project_name'
return "/".join(path_parts[:2]), mr_id
# Handle special delimiter (-)
project_path = "/".join(path_parts[:mr_index])
if project_path.endswith('/-'):
project_path = project_path[:-2]
# Return the path before 'merge_requests' and the ID
return project_path, mr_id
def _get_merge_request(self):
mr = self.gl.projects.get(self.id_project).mergerequests.get(self.id_mr)
return mr
def get_user_id(self):
return None
def publish_labels(self, pr_types):
try:
self.mr.labels = list(set(pr_types))
self.mr.save()
except Exception as e:
get_logger().exception(f"Failed to publish labels, error: {e}")
def publish_inline_comments(self, comments: list[dict]):
pass
def get_pr_labels(self):
return self.mr.labels
def get_repo_labels(self):
return self.gl.projects.get(self.id_project).labels.list()
def get_commit_messages(self):
"""
Retrieves the commit messages of a pull request.
Returns:
str: A string containing the commit messages of the pull request.
"""
max_tokens = get_settings().get("CONFIG.MAX_COMMITS_TOKENS", None)
try:
commit_messages_list = [commit['message'] for commit in self.mr.commits()._list]
commit_messages_str = "\n".join([f"{i + 1}. {message}" for i, message in enumerate(commit_messages_list)])
except Exception:
commit_messages_str = ""
if max_tokens:
commit_messages_str = clip_tokens(commit_messages_str, max_tokens)
return commit_messages_str
def get_pr_id(self):
try:
pr_id = self.mr.web_url
return pr_id
except:
return ""
def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str:
if relevant_line_start == -1:
link = f"{self.gl.url}/{self.id_project}/-/blob/{self.mr.source_branch}/{relevant_file}?ref_type=heads"
elif relevant_line_end:
link = f"{self.gl.url}/{self.id_project}/-/blob/{self.mr.source_branch}/{relevant_file}?ref_type=heads#L{relevant_line_start}-L{relevant_line_end}"
else:
link = f"{self.gl.url}/{self.id_project}/-/blob/{self.mr.source_branch}/{relevant_file}?ref_type=heads#L{relevant_line_start}"
return link
def generate_link_to_relevant_line_number(self, suggestion) -> str:
try:
relevant_file = suggestion['relevant_file'].strip('`').strip("'").rstrip()
relevant_line_str = suggestion['relevant_line'].rstrip()
if not relevant_line_str:
return ""
position, absolute_position = find_line_number_of_relevant_line_in_file \
(self.diff_files, relevant_file, relevant_line_str)
if absolute_position != -1:
# link to right file only
link = f"{self.gl.url}/{self.id_project}/-/blob/{self.mr.source_branch}/{relevant_file}?ref_type=heads#L{absolute_position}"
# # link to diff
# sha_file = hashlib.sha1(relevant_file.encode('utf-8')).hexdigest()
# link = f"{self.pr.web_url}/diffs#{sha_file}_{absolute_position}_{absolute_position}"
return link
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"Failed adding line link, error: {e}")
return ""

View File

@ -0,0 +1,180 @@
from collections import Counter
from pathlib import Path
from typing import List
from git import Repo
from pr_agent.config_loader import _find_repository_root, get_settings
from pr_agent.git_providers.git_provider import GitProvider
from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
from pr_agent.log import get_logger
class PullRequestMimic:
"""
This class mimics the PullRequest class from the PyGithub library for the LocalGitProvider.
"""
def __init__(self, title: str, diff_files: List[FilePatchInfo]):
self.title = title
self.diff_files = diff_files
class LocalGitProvider(GitProvider):
"""
This class implements the GitProvider interface for local git repositories.
It mimics the PR functionality of the GitProvider interface,
but does not require a hosted git repository.
Instead of providing a PR url, the user provides a local branch path to generate a diff-patch.
For the MVP it only supports the /review and /describe capabilities.
"""
def __init__(self, target_branch_name, incremental=False):
self.repo_path = _find_repository_root()
if self.repo_path is None:
raise ValueError('Could not find repository root')
self.repo = Repo(self.repo_path)
self.head_branch_name = self.repo.head.ref.name
self.target_branch_name = target_branch_name
self._prepare_repo()
self.diff_files = None
self.pr = PullRequestMimic(self.get_pr_title(), self.get_diff_files())
self.description_path = get_settings().get('local.description_path') \
if get_settings().get('local.description_path') is not None else self.repo_path / 'description.md'
self.review_path = get_settings().get('local.review_path') \
if get_settings().get('local.review_path') is not None else self.repo_path / 'review.md'
# inline code comments are not supported for local git repositories
get_settings().pr_reviewer.inline_code_comments = False
def _prepare_repo(self):
"""
Prepare the repository for PR-mimic generation.
"""
get_logger().debug('Preparing repository for PR-mimic generation...')
if self.repo.is_dirty():
raise ValueError('The repository is not in a clean state. Please commit or stash pending changes.')
if self.target_branch_name not in self.repo.heads:
raise KeyError(f'Branch: {self.target_branch_name} does not exist')
def is_supported(self, capability: str) -> bool:
if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments', 'get_labels',
'gfm_markdown']:
return False
return True
def get_diff_files(self) -> list[FilePatchInfo]:
diffs = self.repo.head.commit.diff(
self.repo.merge_base(self.repo.head, self.repo.branches[self.target_branch_name]),
create_patch=True,
R=True
)
diff_files = []
for diff_item in diffs:
if diff_item.a_blob is not None:
original_file_content_str = diff_item.a_blob.data_stream.read().decode('utf-8')
else:
original_file_content_str = "" # empty file
if diff_item.b_blob is not None:
new_file_content_str = diff_item.b_blob.data_stream.read().decode('utf-8')
else:
new_file_content_str = "" # empty file
edit_type = EDIT_TYPE.MODIFIED
if diff_item.new_file:
edit_type = EDIT_TYPE.ADDED
elif diff_item.deleted_file:
edit_type = EDIT_TYPE.DELETED
elif diff_item.renamed_file:
edit_type = EDIT_TYPE.RENAMED
diff_files.append(
FilePatchInfo(original_file_content_str,
new_file_content_str,
diff_item.diff.decode('utf-8'),
diff_item.b_path,
edit_type=edit_type,
old_filename=None if diff_item.a_path == diff_item.b_path else diff_item.a_path
)
)
self.diff_files = diff_files
return diff_files
def get_files(self) -> List[str]:
"""
Returns a list of files with changes in the diff.
"""
diff_index = self.repo.head.commit.diff(
self.repo.merge_base(self.repo.head, self.repo.branches[self.target_branch_name]),
R=True
)
# Get the list of changed files
diff_files = [item.a_path for item in diff_index]
return diff_files
def publish_description(self, pr_title: str, pr_body: str):
with open(self.description_path, "w") as file:
# Write the string to the file
file.write(pr_title + '\n' + pr_body)
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
with open(self.review_path, "w") as file:
# Write the string to the file
file.write(pr_comment)
def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
raise NotImplementedError('Publishing inline comments is not implemented for the local git provider')
def publish_inline_comments(self, comments: list[dict]):
raise NotImplementedError('Publishing inline comments is not implemented for the local git provider')
def publish_code_suggestion(self, body: str, relevant_file: str,
relevant_lines_start: int, relevant_lines_end: int):
raise NotImplementedError('Publishing code suggestions is not implemented for the local git provider')
def publish_code_suggestions(self, code_suggestions: list) -> bool:
raise NotImplementedError('Publishing code suggestions is not implemented for the local git provider')
def publish_labels(self, labels):
pass # Not applicable to the local git provider, but required by the interface
def remove_initial_comment(self):
pass # Not applicable to the local git provider, but required by the interface
def remove_comment(self, comment):
pass # Not applicable to the local git provider, but required by the interface
def get_languages(self):
"""
Calculate percentage of languages in repository. Used for hunk prioritisation.
"""
# Get all files in repository
filepaths = [Path(item.path) for item in self.repo.tree().traverse() if item.type == 'blob']
# Identify language by file extension and count
lang_count = Counter(ext.lstrip('.') for filepath in filepaths for ext in [filepath.suffix.lower()])
# Convert counts to percentages
total_files = len(filepaths)
lang_percentage = {lang: count / total_files * 100 for lang, count in lang_count.items()}
return lang_percentage
def get_pr_branch(self):
return self.repo.head
def get_user_id(self):
return -1 # Not used anywhere for the local provider, but required by the interface
def get_pr_description_full(self):
commits_diff = list(self.repo.iter_commits(self.target_branch_name + '..HEAD'))
# Get the commit messages and concatenate
commit_messages = " ".join([commit.message for commit in commits_diff])
# TODO Handle the description better - maybe use gpt-3.5 summarisation here?
return commit_messages[:200] # Use max 200 characters
def get_pr_title(self):
"""
Substitutes the branch-name as the PR-mimic title.
"""
return self.head_branch_name
def get_issue_comments(self):
raise NotImplementedError('Getting issue comments is not implemented for the local git provider')
def get_pr_labels(self):
raise NotImplementedError('Getting labels is not implemented for the local git provider')

View File

@ -0,0 +1,49 @@
import copy
import os
import tempfile
from dynaconf import Dynaconf
from pr_agent.config_loader import get_settings
from pr_agent.git_providers import get_git_provider
from pr_agent.log import get_logger
from starlette_context import context
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
if repo_settings is None: # None is different from "", which is a valid value
git_provider = get_git_provider()(pr_url)
repo_settings = git_provider.get_repo_settings()
try:
context["repo_settings"] = repo_settings
except Exception:
pass
if repo_settings:
repo_settings_file = None
fd, repo_settings_file = tempfile.mkstemp(suffix='.toml')
os.write(fd, repo_settings)
new_settings = Dynaconf(settings_files=[repo_settings_file])
for section, contents in new_settings.as_dict().items():
section_dict = copy.deepcopy(get_settings().as_dict().get(section, {}))
for key, value in contents.items():
section_dict[key] = value
get_settings().unset(section)
get_settings().set(section, section_dict, merge=False)
get_logger().info(f"Applying repo settings:\n{new_settings.as_dict()}")
except Exception as e:
get_logger().exception("Failed to apply repo settings", e)
finally:
if repo_settings_file:
try:
os.remove(repo_settings_file)
except Exception as e:
get_logger().error(f"Failed to remove temporary settings file {repo_settings_file}", e)

View File

@ -0,0 +1,13 @@
from pr_agent.config_loader import get_settings
from pr_agent.identity_providers.default_identity_provider import DefaultIdentityProvider
_IDENTITY_PROVIDERS = {
'default': DefaultIdentityProvider
}
def get_identity_provider():
identity_provider_id = get_settings().get("CONFIG.IDENTITY_PROVIDER", "default")
if identity_provider_id not in _IDENTITY_PROVIDERS:
raise ValueError(f"Unknown identity provider: {identity_provider_id}")
return _IDENTITY_PROVIDERS[identity_provider_id]()

View File

@ -0,0 +1,9 @@
from pr_agent.identity_providers.identity_provider import Eligibility, IdentityProvider
class DefaultIdentityProvider(IdentityProvider):
def verify_eligibility(self, git_provider, git_provider_id, pr_url):
return Eligibility.ELIGIBLE
def inc_invocation_count(self, git_provider, git_provider_id):
pass

View File

@ -0,0 +1,18 @@
from abc import ABC, abstractmethod
from enum import Enum
class Eligibility(Enum):
NOT_ELIGIBLE = 0
ELIGIBLE = 1
TRIAL = 2
class IdentityProvider(ABC):
@abstractmethod
def verify_eligibility(self, git_provider, git_provier_id, pr_url):
pass
@abstractmethod
def inc_invocation_count(self, git_provider, git_provider_id):
pass

65
pr_agent/log/__init__.py Normal file
View File

@ -0,0 +1,65 @@
import json
import logging
import os
import sys
from enum import Enum
from loguru import logger
from pr_agent.config_loader import get_settings
class LoggingFormat(str, Enum):
CONSOLE = "CONSOLE"
JSON = "JSON"
def json_format(record: dict) -> str:
return record["message"]
def analytics_filter(record: dict) -> bool:
return record.get("extra", {}).get("analytics", False)
def inv_analytics_filter(record: dict) -> bool:
return not record.get("extra", {}).get("analytics", False)
def setup_logger(level: str = "INFO", fmt: LoggingFormat = LoggingFormat.CONSOLE):
level: int = logging.getLevelName(level.upper())
if type(level) is not int:
level = logging.INFO
if fmt == LoggingFormat.JSON and os.getenv("LOG_SANE", "0").lower() == "0": # better debugging github_app
logger.remove(None)
logger.add(
sys.stdout,
filter=inv_analytics_filter,
level=level,
format="{message}",
colorize=False,
serialize=True,
)
elif fmt == LoggingFormat.CONSOLE: # does not print the 'extra' fields
logger.remove(None)
logger.add(sys.stdout, level=level, colorize=True, filter=inv_analytics_filter)
log_folder = get_settings().get("CONFIG.ANALYTICS_FOLDER", "")
if log_folder:
pid = os.getpid()
log_file = os.path.join(log_folder, f"pr-agent.{pid}.log")
logger.add(
log_file,
filter=analytics_filter,
level=level,
format="{message}",
colorize=False,
serialize=True,
)
return logger
def get_logger(*args, **kwargs):
return logger

View File

@ -0,0 +1,19 @@
from pr_agent.config_loader import get_settings
def get_secret_provider():
if not get_settings().get("CONFIG.SECRET_PROVIDER"):
return None
provider_id = get_settings().config.secret_provider
if provider_id == 'google_cloud_storage':
try:
from pr_agent.secret_providers.google_cloud_storage_secret_provider import GoogleCloudStorageSecretProvider
return GoogleCloudStorageSecretProvider()
except Exception as e:
raise ValueError(f"Failed to initialize google_cloud_storage secret provider {provider_id}") from e
else:
raise ValueError("Unknown SECRET_PROVIDER")

View File

@ -0,0 +1,34 @@
import ujson
from google.cloud import storage
from pr_agent.config_loader import get_settings
from pr_agent.log import get_logger
from pr_agent.secret_providers.secret_provider import SecretProvider
class GoogleCloudStorageSecretProvider(SecretProvider):
def __init__(self):
try:
self.client = storage.Client.from_service_account_info(ujson.loads(get_settings().google_cloud_storage.
service_account))
self.bucket_name = get_settings().google_cloud_storage.bucket_name
self.bucket = self.client.bucket(self.bucket_name)
except Exception as e:
get_logger().error(f"Failed to initialize Google Cloud Storage Secret Provider: {e}")
raise e
def get_secret(self, secret_name: str) -> str:
try:
blob = self.bucket.blob(secret_name)
return blob.download_as_string()
except Exception as e:
get_logger().error(f"Failed to get secret {secret_name} from Google Cloud Storage: {e}")
return ""
def store_secret(self, secret_name: str, secret_value: str):
try:
blob = self.bucket.blob(secret_name)
blob.upload_from_string(secret_value)
except Exception as e:
get_logger().error(f"Failed to store secret {secret_name} in Google Cloud Storage: {e}")
raise e

View File

@ -0,0 +1,12 @@
from abc import ABC, abstractmethod
class SecretProvider(ABC):
@abstractmethod
def get_secret(self, secret_name: str) -> str:
pass
@abstractmethod
def store_secret(self, secret_name: str, secret_value: str):
pass

View File

@ -0,0 +1,33 @@
{
"name": "CodiumAI PR-Agent",
"description": "CodiumAI PR-Agent",
"key": "app_key",
"vendor": {
"name": "CodiumAI",
"url": "https://codium.ai"
},
"authentication": {
"type": "jwt"
},
"baseUrl": "base_url",
"lifecycle": {
"installed": "/installed",
"uninstalled": "/uninstalled"
},
"scopes": [
"account",
"repository",
"pullrequest"
],
"contexts": [
"account"
],
"modules": {
"webhooks": [
{
"event": "*",
"url": "/webhook"
}
]
}
}

View File

@ -0,0 +1,137 @@
# This file contains the code for the Azure DevOps Server webhook server.
# The server listens for incoming webhooks from Azure DevOps Server and forwards them to the PR Agent.
# ADO webhook documentation: https://learn.microsoft.com/en-us/azure/devops/service-hooks/services/webhooks?view=azure-devops
import json
import os
import re
import secrets
import uvicorn
from fastapi import APIRouter, Depends, FastAPI, HTTPException
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.encoders import jsonable_encoder
from starlette import status
from starlette.background import BackgroundTasks
from starlette.middleware import Middleware
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette_context.middleware import RawContextMiddleware
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.utils import apply_repo_settings
from pr_agent.log import get_logger
from fastapi import Request, Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from pr_agent.log import get_logger
security = HTTPBasic()
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")
def handle_request(
background_tasks: BackgroundTasks, url: str, body: str, log_context: dict
):
log_context["action"] = body
log_context["api_url"] = url
with get_logger().contextualize(**log_context):
background_tasks.add_task(PRAgent().handle_request, url, body)
# 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'},
)
async def _perform_commands_azure(commands_conf: str, agent: PRAgent, api_url: str, log_context: dict):
apply_repo_settings(api_url)
commands = get_settings().get(f"azure_devops_server.{commands_conf}")
for command in commands:
try:
split_command = command.split(" ")
command = split_command[0]
args = split_command[1:]
other_args = update_settings_from_args(args)
new_command = ' '.join([command] + other_args)
get_logger().info(f"Performing command: {new_command}")
with get_logger().contextualize(**log_context):
await agent.handle_request(api_url, new_command)
except Exception as e:
get_logger().error(f"Failed to perform command {command}: {e}")
@router.post("/", dependencies=[Depends(authorize)])
async def handle_webhook(background_tasks: BackgroundTasks, request: Request):
log_context = {"server_type": "azure_devops_server"}
data = await request.json()
get_logger().info(json.dumps(data))
actions = []
if data["eventType"] == "git.pullrequest.created":
# API V1 (latest)
pr_url = data["resource"]["_links"]["web"]["href"].replace("_apis/git/repositories", "_git")
log_context["event"] = data["eventType"]
log_context["api_url"] = pr_url
await _perform_commands_azure("pr_commands", PRAgent(), pr_url, log_context)
return
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"]):
if(data["resourceVersion"] == "2.0"):
repo = data["resource"]["pullRequest"]["repository"]["webUrl"]
pr_url = f'{repo}/pullrequest/{data["resource"]["pullRequest"]["pullRequestId"]}'
actions = [data["resource"]["comment"]["content"]]
else:
# API V1 not supported as it does not contain the PR URL
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=json.dumps({"message": "version 1.0 webhook for Azure Devops PR comment is not supported. please upgrade to version 2.0"})),
else:
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=json.dumps({"message": "Unsupported command"}),
)
else:
return JSONResponse(
status_code=status.HTTP_204_NO_CONTENT,
content=json.dumps({"message": "Unsupported event"}),
)
log_context["event"] = data["eventType"]
log_context["api_url"] = pr_url
for action in actions:
try:
handle_request(background_tasks, 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"}),
)
return JSONResponse(
status_code=status.HTTP_202_ACCEPTED, content=jsonable_encoder({"message": "webhook triggerd successfully"})
)
@router.get("/")
async def root():
return {"status": "ok"}
def start():
app = FastAPI(middleware=[Middleware(RawContextMiddleware)])
app.include_router(router)
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "3000")))
if __name__ == "__main__":
start()

View File

@ -0,0 +1,190 @@
import base64
import copy
import hashlib
import json
import os
import time
import jwt
import requests
import uvicorn
from fastapi import APIRouter, FastAPI, Request, Response
from starlette.background import BackgroundTasks
from starlette.middleware import Middleware
from starlette.responses import JSONResponse
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.git_providers.utils import apply_repo_settings
from pr_agent.identity_providers import get_identity_provider
from pr_agent.identity_providers.identity_provider import Eligibility
from pr_agent.log import LoggingFormat, get_logger, setup_logger
from pr_agent.secret_providers import get_secret_provider
from pr_agent.servers.github_action_runner import get_setting_or_env, is_true
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
setup_logger(fmt=LoggingFormat.JSON)
router = APIRouter()
secret_provider = get_secret_provider() if get_settings().get("CONFIG.SECRET_PROVIDER") else None
async def get_bearer_token(shared_secret: str, client_key: str):
try:
now = int(time.time())
url = "https://bitbucket.org/site/oauth2/access_token"
canonical_url = "GET&/site/oauth2/access_token&"
qsh = hashlib.sha256(canonical_url.encode("utf-8")).hexdigest()
app_key = get_settings().bitbucket.app_key
payload = {
"iss": app_key,
"iat": now,
"exp": now + 240,
"qsh": qsh,
"sub": client_key,
}
token = jwt.encode(payload, shared_secret, algorithm="HS256")
payload = 'grant_type=urn%3Abitbucket%3Aoauth2%3Ajwt'
headers = {
'Authorization': f'JWT {token}',
'Content-Type': 'application/x-www-form-urlencoded'
}
response = requests.request("POST", url, headers=headers, data=payload)
bearer_token = response.json()["access_token"]
return bearer_token
except Exception as e:
get_logger().error(f"Failed to get bearer token: {e}")
raise e
@router.get("/")
async def handle_manifest(request: Request, response: Response):
cur_dir = os.path.dirname(os.path.abspath(__file__))
manifest = open(os.path.join(cur_dir, "atlassian-connect.json"), "rt").read()
try:
manifest = manifest.replace("app_key", get_settings().bitbucket.app_key)
manifest = manifest.replace("base_url", get_settings().bitbucket.base_url)
except:
get_logger().error("Failed to replace api_key in Bitbucket manifest, trying to continue")
manifest_obj = json.loads(manifest)
return JSONResponse(manifest_obj)
@router.post("/webhook")
async def handle_github_webhooks(background_tasks: BackgroundTasks, request: Request):
log_context = {"server_type": "bitbucket_app"}
get_logger().debug(request.headers)
jwt_header = request.headers.get("authorization", None)
if jwt_header:
input_jwt = jwt_header.split(" ")[1]
data = await request.json()
get_logger().debug(data)
async def inner():
try:
try:
if data["data"]["actor"]["type"] != "user":
return "OK"
except KeyError:
get_logger().error("Failed to get actor type, check previous logs, this shouldn't happen.")
try:
owner = data["data"]["repository"]["owner"]["username"]
except Exception as e:
get_logger().error(f"Failed to get owner, will continue: {e}")
owner = "unknown"
sender_id = data["data"]["actor"]["account_id"]
log_context["sender"] = owner
log_context["sender_id"] = sender_id
jwt_parts = input_jwt.split(".")
claim_part = jwt_parts[1]
claim_part += "=" * (-len(claim_part) % 4)
decoded_claims = base64.urlsafe_b64decode(claim_part)
claims = json.loads(decoded_claims)
client_key = claims["iss"]
secrets = json.loads(secret_provider.get_secret(client_key))
shared_secret = secrets["shared_secret"]
jwt.decode(input_jwt, shared_secret, audience=client_key, algorithms=["HS256"])
bearer_token = await get_bearer_token(shared_secret, client_key)
context['bitbucket_bearer_token'] = bearer_token
context["settings"] = copy.deepcopy(global_settings)
event = data["event"]
agent = PRAgent()
if event == "pullrequest:created":
pr_url = data["data"]["pullrequest"]["links"]["html"]["href"]
log_context["api_url"] = pr_url
log_context["event"] = "pull_request"
if pr_url:
with get_logger().contextualize(**log_context):
apply_repo_settings(pr_url)
if get_identity_provider().verify_eligibility("bitbucket",
sender_id, pr_url) is not Eligibility.NOT_ELIGIBLE:
auto_review = get_setting_or_env("BITBUCKET_APP.AUTO_REVIEW", None)
if auto_review is None or is_true(auto_review): # by default, auto review is enabled
await PRReviewer(pr_url).run()
auto_improve = get_setting_or_env("BITBUCKET_APP.AUTO_IMPROVE", None)
if is_true(auto_improve): # by default, auto improve is disabled
await PRCodeSuggestions(pr_url).run()
auto_describe = get_setting_or_env("BITBUCKET_APP.AUTO_DESCRIBE", None)
if is_true(auto_describe): # by default, auto describe is disabled
await PRDescription(pr_url).run()
# with get_logger().contextualize(**log_context):
# await agent.handle_request(pr_url, "review")
elif event == "pullrequest:comment_created":
pr_url = data["data"]["pullrequest"]["links"]["html"]["href"]
log_context["api_url"] = pr_url
log_context["event"] = "comment"
comment_body = data["data"]["comment"]["content"]["raw"]
with get_logger().contextualize(**log_context):
if get_identity_provider().verify_eligibility("bitbucket",
sender_id, pr_url) is not Eligibility.NOT_ELIGIBLE:
await agent.handle_request(pr_url, comment_body)
except Exception as e:
get_logger().error(f"Failed to handle webhook: {e}")
background_tasks.add_task(inner)
return "OK"
@router.get("/webhook")
async def handle_github_webhooks(request: Request, response: Response):
return "Webhook server online!"
@router.post("/installed")
async def handle_installed_webhooks(request: Request, response: Response):
try:
get_logger().info("handle_installed_webhooks")
get_logger().info(request.headers)
data = await request.json()
get_logger().info(data)
shared_secret = data["sharedSecret"]
client_key = data["clientKey"]
username = data["principal"]["username"]
secrets = {
"shared_secret": shared_secret,
"client_key": client_key
}
secret_provider.store_secret(username, json.dumps(secrets))
except Exception as e:
get_logger().error(f"Failed to register user: {e}")
return JSONResponse({"error": "Unable to register user"}, status_code=500)
@router.post("/uninstalled")
async def handle_uninstalled_webhooks(request: Request, response: Response):
get_logger().info("handle_uninstalled_webhooks")
data = await request.json()
get_logger().info(data)
def start():
get_settings().set("CONFIG.PUBLISH_OUTPUT_PROGRESS", False)
get_settings().set("CONFIG.GIT_PROVIDER", "bitbucket")
get_settings().set("PR_DESCRIPTION.PUBLISH_DESCRIPTION_AS_COMMENT", True)
middleware = [Middleware(RawContextMiddleware)]
app = FastAPI(middleware=middleware)
app.include_router(router)
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "3000")))
if __name__ == '__main__':
start()

View File

@ -0,0 +1,80 @@
import json
import os
import uvicorn
from fastapi import APIRouter, FastAPI
from fastapi.encoders import jsonable_encoder
from starlette import status
from starlette.background import BackgroundTasks
from starlette.middleware import Middleware
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette_context.middleware import RawContextMiddleware
from pr_agent.agent.pr_agent import PRAgent
from pr_agent.config_loader import get_settings
from pr_agent.log import get_logger
from pr_agent.servers.utils import verify_signature
router = APIRouter()
def handle_request(
background_tasks: BackgroundTasks, url: str, body: str, log_context: dict
):
log_context["action"] = body
log_context["api_url"] = url
with get_logger().contextualize(**log_context):
background_tasks.add_task(PRAgent().handle_request, url, body)
@router.post("/")
async def handle_webhook(background_tasks: BackgroundTasks, request: Request):
log_context = {"server_type": "bitbucket_server"}
data = await request.json()
get_logger().info(json.dumps(data))
webhook_secret = get_settings().get("BITBUCKET_SERVER.WEBHOOK_SECRET", None)
if webhook_secret:
body_bytes = await request.body()
signature_header = request.headers.get("x-hub-signature", None)
verify_signature(body_bytes, webhook_secret, signature_header)
pr_id = data["pullRequest"]["id"]
repository_name = data["pullRequest"]["toRef"]["repository"]["slug"]
project_name = data["pullRequest"]["toRef"]["repository"]["project"]["key"]
bitbucket_server = get_settings().get("BITBUCKET_SERVER.URL")
pr_url = f"{bitbucket_server}/projects/{project_name}/repos/{repository_name}/pull-requests/{pr_id}"
log_context["api_url"] = pr_url
log_context["event"] = "pull_request"
if data["eventKey"] == "pr:opened":
body = "review"
elif data["eventKey"] == "pr:comment:added":
body = data["comment"]["text"]
else:
return JSONResponse(
status_code=status.HTTP_400_BAD_REQUEST,
content=json.dumps({"message": "Unsupported event"}),
)
handle_request(background_tasks, pr_url, body, log_context)
return JSONResponse(
status_code=status.HTTP_200_OK, content=jsonable_encoder({"message": "success"})
)
@router.get("/")
async def root():
return {"status": "ok"}
def start():
app = FastAPI(middleware=[Middleware(RawContextMiddleware)])
app.include_router(router)
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "3000")))
if __name__ == "__main__":
start()

View File

@ -0,0 +1,77 @@
import copy
from enum import Enum
from json import JSONDecodeError
import uvicorn
from fastapi import APIRouter, FastAPI, HTTPException
from pydantic import BaseModel
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 get_logger, setup_logger
setup_logger()
router = APIRouter()
class Action(str, Enum):
review = "review"
describe = "describe"
ask = "ask"
improve = "improve"
reflect = "reflect"
answer = "answer"
class Item(BaseModel):
refspec: str
project: str
msg: str
@router.post("/api/v1/gerrit/{action}")
async def handle_gerrit_request(action: Action, item: Item):
get_logger().debug("Received a Gerrit request")
context["settings"] = copy.deepcopy(global_settings)
if action == Action.ask:
if not item.msg:
return HTTPException(
status_code=400,
detail="msg is required for ask command"
)
await PRAgent().handle_request(
f"{item.project}:{item.refspec}",
f"/{item.msg.strip()}"
)
async def get_body(request):
try:
body = await request.json()
except JSONDecodeError as e:
get_logger().error("Error parsing request body", e)
return {}
return body
@router.get("/")
async def root():
return {"status": "ok"}
def start():
# to prevent adding help messages with the output
get_settings().set("CONFIG.CLI_MODE", True)
middleware = [Middleware(RawContextMiddleware)]
app = FastAPI(middleware=middleware)
app.include_router(router)
uvicorn.run(app, host="0.0.0.0", port=3000)
if __name__ == '__main__':
start()

View File

@ -0,0 +1,143 @@
import asyncio
import json
import os
from typing import Union
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.git_providers.utils import apply_repo_settings
from pr_agent.log import get_logger
from pr_agent.servers.github_app import handle_line_comments
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
def is_true(value: Union[str, bool]) -> bool:
if isinstance(value, bool):
return value
if isinstance(value, str):
return value.lower() == 'true'
return False
def get_setting_or_env(key: str, default: Union[str, bool] = None) -> Union[str, bool]:
try:
value = get_settings().get(key, default)
except AttributeError: # TBD still need to debug why this happens on GitHub Actions
value = os.getenv(key, None) or os.getenv(key.upper(), None) or os.getenv(key.lower(), None) or default
return value
async def run_action():
# Get environment variables
GITHUB_EVENT_NAME = os.environ.get('GITHUB_EVENT_NAME')
GITHUB_EVENT_PATH = os.environ.get('GITHUB_EVENT_PATH')
OPENAI_KEY = os.environ.get('OPENAI_KEY') or os.environ.get('OPENAI.KEY')
OPENAI_ORG = os.environ.get('OPENAI_ORG') or os.environ.get('OPENAI.ORG')
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN')
get_settings().set("CONFIG.PUBLISH_OUTPUT_PROGRESS", False)
# Check if required environment variables are set
if not GITHUB_EVENT_NAME:
print("GITHUB_EVENT_NAME not set")
return
if not GITHUB_EVENT_PATH:
print("GITHUB_EVENT_PATH not set")
return
if not OPENAI_KEY:
print("OPENAI_KEY not set")
return
if not GITHUB_TOKEN:
print("GITHUB_TOKEN not set")
return
# Set the environment variables in the settings
get_settings().set("OPENAI.KEY", OPENAI_KEY)
if OPENAI_ORG:
get_settings().set("OPENAI.ORG", OPENAI_ORG)
get_settings().set("GITHUB.USER_TOKEN", GITHUB_TOKEN)
get_settings().set("GITHUB.DEPLOYMENT_TYPE", "user")
# Load the event payload
try:
with open(GITHUB_EVENT_PATH, 'r') as f:
event_payload = json.load(f)
except json.decoder.JSONDecodeError as e:
print(f"Failed to parse JSON: {e}")
return
try:
get_logger().info("Applying repo settings")
pr_url = event_payload.get("pull_request", {}).get("html_url")
if pr_url:
apply_repo_settings(pr_url)
get_logger().info(f"enable_custom_labels: {get_settings().config.enable_custom_labels}")
except Exception as e:
get_logger().info(f"github action: failed to apply repo settings: {e}")
# Handle pull request event
if GITHUB_EVENT_NAME == "pull_request":
action = event_payload.get("action")
if action in ["opened", "reopened", "ready_for_review", "review_requested"]:
pr_url = event_payload.get("pull_request", {}).get("url")
if pr_url:
# legacy - supporting both GITHUB_ACTION and GITHUB_ACTION_CONFIG
auto_review = get_setting_or_env("GITHUB_ACTION.AUTO_REVIEW", None)
if auto_review is None:
auto_review = get_setting_or_env("GITHUB_ACTION_CONFIG.AUTO_REVIEW", None)
auto_describe = get_setting_or_env("GITHUB_ACTION.AUTO_DESCRIBE", None)
if auto_describe is None:
auto_describe = get_setting_or_env("GITHUB_ACTION_CONFIG.AUTO_DESCRIBE", None)
auto_improve = get_setting_or_env("GITHUB_ACTION.AUTO_IMPROVE", None)
if auto_improve is None:
auto_improve = get_setting_or_env("GITHUB_ACTION_CONFIG.AUTO_IMPROVE", None)
# invoke by default all three tools
if auto_describe is None or is_true(auto_describe):
await PRDescription(pr_url).run()
if auto_review is None or is_true(auto_review):
await PRReviewer(pr_url).run()
if auto_improve is None or is_true(auto_improve):
await PRCodeSuggestions(pr_url).run()
# Handle issue comment event
elif GITHUB_EVENT_NAME == "issue_comment" or GITHUB_EVENT_NAME == "pull_request_review_comment":
action = event_payload.get("action")
if action in ["created", "edited"]:
comment_body = event_payload.get("comment", {}).get("body")
try:
if GITHUB_EVENT_NAME == "pull_request_review_comment":
if '/ask' in comment_body:
comment_body = handle_line_comments(event_payload, comment_body)
except Exception as e:
get_logger().error(f"Failed to handle line comments: {e}")
return
if comment_body:
is_pr = False
disable_eyes = False
# check if issue is pull request
if event_payload.get("issue", {}).get("pull_request"):
url = event_payload.get("issue", {}).get("pull_request", {}).get("url")
is_pr = True
elif event_payload.get("comment", {}).get("pull_request_url"): # for 'pull_request_review_comment
url = event_payload.get("comment", {}).get("pull_request_url")
is_pr = True
disable_eyes = True
else:
url = event_payload.get("issue", {}).get("url")
if url:
body = comment_body.strip().lower()
comment_id = event_payload.get("comment", {}).get("id")
provider = get_git_provider()(pr_url=url)
if is_pr:
await PRAgent().handle_request(url, body,
notify=lambda: provider.add_eyes_reaction(comment_id, disable_eyes=disable_eyes))
else:
await PRAgent().handle_request(url, body)
if __name__ == '__main__':
asyncio.run(run_action())

View File

@ -1,64 +1,328 @@
import logging
import sys
import asyncio.locks
import copy
import os
import re
import uuid
from typing import Any, Dict, Tuple
import uvicorn
from fastapi import APIRouter, FastAPI, HTTPException, Request, Response
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 settings
from pr_agent.servers.utils import verify_signature
from pr_agent.algo.utils import update_settings_from_args
from pr_agent.config_loader import get_settings, global_settings
from pr_agent.git_providers import get_git_provider
from pr_agent.git_providers.git_provider import IncrementalPR
from pr_agent.git_providers.utils import apply_repo_settings
from pr_agent.identity_providers import get_identity_provider
from pr_agent.identity_providers.identity_provider import Eligibility
from pr_agent.log import LoggingFormat, get_logger, setup_logger
from pr_agent.servers.utils import DefaultDictWithTimeout, verify_signature
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
setup_logger(fmt=LoggingFormat.JSON, 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):
with open(build_number_path) as f:
build_number = f.read().strip()
else:
build_number = "unknown"
router = APIRouter()
@router.post("/api/v1/github_webhooks")
async def handle_github_webhooks(request: Request, response: Response):
logging.debug("Received a github webhook")
"""
Receives and processes incoming GitHub webhook requests.
Verifies the request signature, parses the request body, and passes it to the handle_request function for further
processing.
"""
get_logger().debug("Received a GitHub webhook")
body = await get_body(request)
installation_id = body.get("installation", {}).get("id")
context["installation_id"] = installation_id
context["settings"] = copy.deepcopy(global_settings)
response = await handle_request(body, event=request.headers.get("X-GitHub-Event", None))
return response or {}
@router.post("/api/v1/marketplace_webhooks")
async def handle_marketplace_webhooks(request: Request, response: Response):
body = await get_body(request)
get_logger().info(f'Request body:\n{body}')
async def get_body(request):
try:
body = await request.json()
except Exception as e:
logging.error("Error parsing request body", e)
get_logger().error("Error parsing request body", e)
raise HTTPException(status_code=400, detail="Error parsing request body") from e
body_bytes = await request.body()
signature_header = request.headers.get('x-hub-signature-256', None)
try:
webhook_secret = settings.github.webhook_secret
except AttributeError:
webhook_secret = None
webhook_secret = getattr(get_settings().github, 'webhook_secret', None)
if webhook_secret:
body_bytes = await request.body()
signature_header = request.headers.get('x-hub-signature-256', None)
verify_signature(body_bytes, webhook_secret, signature_header)
logging.debug(f'Request body:\n{body}')
return await handle_request(body)
return body
async def handle_request(body):
action = body.get("action", None)
installation_id = body.get("installation", {}).get("id", None)
settings.set("GITHUB.INSTALLATION_ID", installation_id)
agent = PRAgent()
if action == 'created':
if "comment" not in body:
return {}
comment_body = body.get("comment", {}).get("body", None)
if 'sender' in body and 'login' in body['sender'] and 'bot' in body['sender']['login']:
return {}
if "issue" not in body and "pull_request" not in body["issue"]:
return {}
pull_request = body["issue"]["pull_request"]
api_url = pull_request.get("url", None)
await agent.handle_request(api_url, comment_body)
_duplicate_push_triggers = DefaultDictWithTimeout(ttl=get_settings().github_app.push_trigger_pending_tasks_ttl)
_pending_task_duplicate_push_conditions = DefaultDictWithTimeout(asyncio.locks.Condition, ttl=get_settings().github_app.push_trigger_pending_tasks_ttl)
elif action in ["opened"] or 'reopened' in action:
pull_request = body.get("pull_request", None)
if not pull_request:
return {}
api_url = pull_request.get("url", None)
if api_url is None:
return {}
await agent.handle_request(api_url, "please review")
async def handle_comments_on_pr(body: Dict[str, Any],
event: str,
sender: str,
sender_id: str,
action: str,
log_context: Dict[str, Any],
agent: PRAgent):
if "comment" not in body:
return {}
comment_body = body.get("comment", {}).get("body")
if comment_body and isinstance(comment_body, str) and not comment_body.lstrip().startswith("/"):
get_logger().info("Ignoring comment not starting with /")
return {}
disable_eyes = False
if "issue" in body and "pull_request" in body["issue"] and "url" in body["issue"]["pull_request"]:
api_url = body["issue"]["pull_request"]["url"]
elif "comment" in body and "pull_request_url" in body["comment"]:
api_url = body["comment"]["pull_request_url"]
try:
if ('/ask' in comment_body and
'subject_type' in body["comment"] and body["comment"]["subject_type"] == "line"):
# comment on a code line in the "files changed" tab
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}")
else:
return {}
log_context["api_url"] = api_url
comment_id = body.get("comment", {}).get("id")
provider = get_git_provider()(pr_url=api_url)
with get_logger().contextualize(**log_context):
if get_identity_provider().verify_eligibility("github", sender_id, api_url) is not Eligibility.NOT_ELIGIBLE:
get_logger().info(f"Processing comment on PR {api_url=}, comment_body={comment_body}")
await agent.handle_request(api_url, comment_body,
notify=lambda: provider.add_eyes_reaction(comment_id, disable_eyes=disable_eyes))
else:
get_logger().info(f"User {sender=} is not eligible to process comment on PR {api_url=}")
async def handle_new_pr_opened(body: Dict[str, Any],
event: str,
sender: str,
sender_id: str,
action: str,
log_context: Dict[str, Any],
agent: PRAgent):
title = body.get("pull_request", {}).get("title", "")
# logic to ignore PRs with specific titles (e.g. "[Auto] ...")
ignore_pr_title_re = get_settings().get("GITHUB_APP.IGNORE_PR_TITLE", [])
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):
get_logger().info(f"Ignoring PR with title '{title}' due to github_app.ignore_pr_title setting")
return {}
pull_request, api_url = _check_pull_request_event(action, body, log_context)
if not (pull_request and api_url):
get_logger().info(f"Invalid PR event: {action=} {api_url=}")
return {}
if action in get_settings().github_app.handle_pr_actions: # ['opened', 'reopened', 'ready_for_review', 'review_requested']
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)
else:
get_logger().info(f"User {sender=} is not eligible to process PR {api_url=}")
async def handle_push_trigger_for_new_commits(body: Dict[str, Any],
event: str,
sender: str,
sender_id: str,
action: str,
log_context: Dict[str, Any],
agent: PRAgent):
pull_request, api_url = _check_pull_request_event(action, body, log_context)
if not (pull_request and api_url):
return {}
apply_repo_settings(api_url) # we need to apply the repo settings to get the correct settings for the PR. This is quite expensive - a call to the git provider is made for each PR event.
if not get_settings().github_app.handle_push_trigger:
return {}
# TODO: do we still want to get the list of commits to filter bot/merge commits?
before_sha = body.get("before")
after_sha = body.get("after")
merge_commit_sha = pull_request.get("merge_commit_sha")
if before_sha == after_sha:
return {}
if get_settings().github_app.push_trigger_ignore_merge_commits and after_sha == merge_commit_sha:
return {}
# Prevent triggering multiple times for subsequent push triggers when one is enough:
# The first push will trigger the processing, and if there's a second push in the meanwhile it will wait.
# Any more events will be discarded, because they will all trigger the exact same processing on the PR.
# We let the second event wait instead of discarding it because while the first event was being processed,
# more commits may have been pushed that led to the subsequent events,
# so we keep just one waiting as a delegate to trigger the processing for the new commits when done waiting.
current_active_tasks = _duplicate_push_triggers.setdefault(api_url, 0)
max_active_tasks = 2 if get_settings().github_app.push_trigger_pending_tasks_backlog else 1
if current_active_tasks < max_active_tasks:
# first task can enter, and second tasks too if backlog is enabled
get_logger().info(
f"Continue processing push trigger for {api_url=} because there are {current_active_tasks} active tasks"
)
_duplicate_push_triggers[api_url] += 1
else:
get_logger().info(
f"Skipping push trigger for {api_url=} because another event already triggered the same processing"
)
return {}
async with _pending_task_duplicate_push_conditions[api_url]:
if current_active_tasks == 1:
# second task waits
get_logger().info(
f"Waiting to process push trigger for {api_url=} because the first task is still in progress"
)
await _pending_task_duplicate_push_conditions[api_url].wait()
get_logger().info(f"Finished waiting to process push trigger for {api_url=} - continue with flow")
try:
if get_settings().github_app.push_trigger_wait_for_initial_review and not get_git_provider()(api_url,
incremental=IncrementalPR(
True)).previous_review:
get_logger().info(f"Skipping incremental review because there was no initial review for {api_url=} yet")
return {}
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)
finally:
# release the waiting task block
async with _pending_task_duplicate_push_conditions[api_url]:
_pending_task_duplicate_push_conditions[api_url].notify(1)
_duplicate_push_triggers[api_url] -= 1
def handle_closed_pr(body, event, action, log_context):
pull_request = body.get("pull_request", {})
is_merged = pull_request.get("merged", False)
if not is_merged:
return
api_url = pull_request.get("url", "")
pr_statistics = get_git_provider()(pr_url=api_url).calc_pr_statistics(pull_request)
log_context["api_url"] = api_url
get_logger().info("PR-Agent statistics for closed PR", analytics=True, pr_statistics=pr_statistics, **log_context)
def get_log_context(body, event, action, build_number):
sender = ""
sender_id = ""
try:
sender = body.get("sender", {}).get("login")
sender_id = body.get("sender", {}).get("id")
repo = body.get("repository", {}).get("full_name", "")
git_org = body.get("organization", {}).get("login", "")
app_name = get_settings().get("CONFIG.APP_NAME", "Unknown")
log_context = {"action": action, "event": event, "sender": sender, "server_type": "github_app",
"request_id": uuid.uuid4().hex, "build_number": build_number, "app_name": app_name,
"repo": repo, "git_org": git_org}
except Exception as e:
get_logger().error("Failed to get log context", e)
log_context = {}
return log_context, sender, sender_id
async def handle_request(body: Dict[str, Any], event: str):
"""
Handle incoming GitHub webhook requests.
Args:
body: The request body.
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"
if not action:
return {}
agent = PRAgent()
log_context, sender, sender_id = get_log_context(body, event, action, build_number)
# handle comments on PRs
if action == 'created':
get_logger().debug(f'Request body', artifact=body, event=event)
await handle_comments_on_pr(body, event, sender, sender_id, action, log_context, agent)
# handle new PRs
elif event == 'pull_request' and action != 'synchronize' and action != 'closed':
get_logger().debug(f'Request body', artifact=body, event=event)
await handle_new_pr_opened(body, event, sender, sender_id, action, log_context, agent)
# handle pull_request event with synchronize action - "push trigger" for new commits
elif event == 'pull_request' and action == 'synchronize':
get_logger().debug(f'Request body', artifact=body, event=event)
await handle_push_trigger_for_new_commits(body, event, sender, sender_id, action, log_context, agent)
elif event == 'pull_request' and action == 'closed':
if get_settings().get("CONFIG.ANALYTICS_FOLDER", ""):
handle_closed_pr(body, event, action, log_context)
else:
get_logger().info(f"event {event=} action {action=} does not require any handling")
return {}
def handle_line_comments(body: Dict, comment_body: [str, Any]) -> str:
if not comment_body:
return ""
start_line = body["comment"]["start_line"]
end_line = body["comment"]["line"]
start_line = end_line if not start_line else start_line
question = comment_body.replace('/ask', '').strip()
diff_hunk = body["comment"]["diff_hunk"]
get_settings().set("ask_diff_hunk", diff_hunk)
path = body["comment"]["path"]
side = body["comment"]["side"]
comment_id = body["comment"]["id"]
if '/ask' in comment_body:
comment_body = f"/ask_line --line_start={start_line} --line_end={end_line} --side={side} --file_name={path} --comment_id={comment_id} {question}"
return comment_body
def _check_pull_request_event(action: str, body: dict, log_context: dict) -> Tuple[Dict[str, Any], str]:
invalid_result = {}, ""
pull_request = body.get("pull_request")
if not pull_request:
return invalid_result
api_url = pull_request.get("url")
if not api_url:
return invalid_result
log_context["api_url"] = api_url
if pull_request.get("draft", True) or pull_request.get("state") != "open":
return invalid_result
if action in ("review_requested", "synchronize") and pull_request.get("created_at") == pull_request.get("updated_at"):
# avoid double reviews when opening a PR for the first time
return invalid_result
return pull_request, api_url
async def _perform_auto_commands_github(commands_conf: str, agent: PRAgent, body: dict, api_url: str, log_context: dict):
apply_repo_settings(api_url)
commands = get_settings().get(f"github_app.{commands_conf}")
if not commands:
with get_logger().contextualize(**log_context):
get_logger().info(f"New PR, but no auto commands configured")
return
for command in commands:
split_command = command.split(" ")
command = split_command[0]
args = split_command[1:]
other_args = update_settings_from_args(args)
new_command = ' '.join([command] + other_args)
with get_logger().contextualize(**log_context):
get_logger().info(f"{commands_conf}. Performing auto command '{new_command}', for {api_url=}")
await agent.handle_request(api_url, new_command)
@router.get("/")
@ -66,14 +330,17 @@ async def root():
return {"status": "ok"}
def start():
if get_settings().github_app.override_deployment_type:
# Override the deployment type to app
settings.set("GITHUB.DEPLOYMENT_TYPE", "app")
app = FastAPI()
app.include_router(router)
get_settings().set("GITHUB.DEPLOYMENT_TYPE", "app")
get_settings().set("CONFIG.PUBLISH_OUTPUT_PROGRESS", False)
middleware = [Middleware(RawContextMiddleware)]
app = FastAPI(middleware=middleware)
app.include_router(router)
uvicorn.run(app, host="0.0.0.0", port=3000)
def start():
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "3000")))
if __name__ == '__main__':
start()

View File

@ -1,40 +1,53 @@
import asyncio
import logging
import sys
from datetime import datetime, timezone
import aiohttp
from pr_agent.agent.pr_agent import PRAgent
from pr_agent.config_loader import settings
from pr_agent.config_loader import get_settings
from pr_agent.git_providers import get_git_provider
from pr_agent.log import LoggingFormat, get_logger, setup_logger
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
setup_logger(fmt=LoggingFormat.JSON)
NOTIFICATION_URL = "https://api.github.com/notifications"
def now() -> str:
"""
Get the current UTC time in ISO 8601 format.
Returns:
str: The current UTC time in ISO 8601 format.
"""
now_utc = datetime.now(timezone.utc).isoformat()
now_utc = now_utc.replace("+00:00", "Z")
return now_utc
async def polling_loop():
"""
Polls for notifications and handles them accordingly.
"""
handled_ids = set()
since = [now()]
last_modified = [None]
git_provider = get_git_provider()()
user_id = git_provider.get_user_id()
agent = PRAgent()
get_settings().set("CONFIG.PUBLISH_OUTPUT_PROGRESS", False)
try:
deployment_type = settings.github.deployment_type
token = settings.github.user_token
deployment_type = get_settings().github.deployment_type
token = get_settings().github.user_token
except AttributeError:
deployment_type = 'none'
token = None
if deployment_type != 'user':
raise ValueError("Deployment mode must be set to 'user' to get notifications")
if not token:
raise ValueError("User token must be set to get notifications")
async with aiohttp.ClientSession() as session:
while True:
try:
@ -50,6 +63,7 @@ async def polling_loop():
params["since"] = since[0]
if last_modified[0]:
headers["If-Modified-Since"] = last_modified[0]
async with session.get(NOTIFICATION_URL, headers=headers, params=params) as response:
if response.status == 200:
if 'Last-Modified' in response.headers:
@ -78,18 +92,24 @@ async def polling_loop():
comment_body = comment['body'] if 'body' in comment else ''
commenter_github_user = comment['user']['login'] \
if 'user' in comment else ''
logging.info(f"Commenter: {commenter_github_user}\nComment: {comment_body}")
get_logger().info(f"Commenter: {commenter_github_user}\nComment: {comment_body}")
user_tag = "@" + user_id
if user_tag not in comment_body:
continue
rest_of_comment = comment_body.split(user_tag)[1].strip()
agent = PRAgent()
await agent.handle_request(pr_url, rest_of_comment)
comment_id = comment['id']
git_provider.set_pr(pr_url)
success = await agent.handle_request(pr_url, rest_of_comment,
notify=lambda: git_provider.add_eyes_reaction(comment_id)) # noqa E501
if not success:
git_provider.set_pr(pr_url)
elif response.status != 304:
print(f"Failed to fetch notifications. Status code: {response.status}")
except Exception as e:
logging.error(f"Exception during processing of a notification: {e}")
get_logger().error(f"Exception during processing of a notification: {e}")
if __name__ == '__main__':
asyncio.run(polling_loop())
asyncio.run(polling_loop())

View File

@ -1,64 +0,0 @@
import asyncio
import time
import gitlab
from pr_agent.agent.pr_agent import PRAgent
from pr_agent.config_loader import settings
gl = gitlab.Gitlab(
settings.get("GITLAB.URL"),
private_token=settings.get("GITLAB.PERSONAL_ACCESS_TOKEN")
)
# Set the list of projects to monitor
projects_to_monitor = settings.get("GITLAB.PROJECTS_TO_MONITOR")
magic_word = settings.get("GITLAB.MAGIC_WORD")
# Hold the previous seen comments
previous_comments = set()
def check_comments():
print('Polling')
new_comments = {}
for project in projects_to_monitor:
project = gl.projects.get(project)
merge_requests = project.mergerequests.list(state='opened')
for mr in merge_requests:
notes = mr.notes.list(get_all=True)
for note in notes:
if note.id not in previous_comments and note.body.startswith(magic_word):
new_comments[note.id] = dict(
body=note.body[len(magic_word):],
project=project.name,
mr=mr
)
previous_comments.add(note.id)
print(f"New comment in project {project.name}, merge request {mr.title}: {note.body}")
return new_comments
def handle_new_comments(new_comments):
print('Handling new comments')
agent = PRAgent()
for _, comment in new_comments.items():
print(f"Handling comment: {comment['body']}")
asyncio.run(agent.handle_request(comment['mr'].web_url, comment['body']))
def run():
assert settings.get('CONFIG.GIT_PROVIDER') == 'gitlab', 'This script is only for GitLab'
# Initial run to populate previous_comments
check_comments()
# Run the check every minute
while True:
time.sleep(settings.get("GITLAB.POLLING_INTERVAL_SECONDS"))
new_comments = check_comments()
if new_comments:
handle_new_comments(new_comments)
if __name__ == '__main__':
run()

View File

@ -0,0 +1,126 @@
import copy
import json
import uvicorn
from fastapi import APIRouter, FastAPI, Request, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from starlette.background import BackgroundTasks
from 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.algo.utils import update_settings_from_args
from pr_agent.config_loader import get_settings, global_settings
from pr_agent.git_providers.utils import apply_repo_settings
from pr_agent.log import LoggingFormat, get_logger, setup_logger
from pr_agent.secret_providers import get_secret_provider
setup_logger(fmt=LoggingFormat.JSON)
router = APIRouter()
secret_provider = get_secret_provider() if get_settings().get("CONFIG.SECRET_PROVIDER") else None
def handle_request(background_tasks: BackgroundTasks, url: str, body: str, log_context: dict):
log_context["action"] = body
log_context["event"] = "pull_request" if body == "/review" else "comment"
log_context["api_url"] = url
with get_logger().contextualize(**log_context):
background_tasks.add_task(PRAgent().handle_request, url, body)
async def _perform_commands_gitlab(commands_conf: str, agent: PRAgent, api_url: str, log_context: dict):
apply_repo_settings(api_url)
commands = get_settings().get(f"gitlab.{commands_conf}", {})
for command in commands:
try:
split_command = command.split(" ")
command = split_command[0]
args = split_command[1:]
other_args = update_settings_from_args(args)
new_command = ' '.join([command] + other_args)
get_logger().info(f"Performing command: {new_command}")
with get_logger().contextualize(**log_context):
await agent.handle_request(api_url, new_command)
except Exception as e:
get_logger().error(f"Failed to perform command {command}: {e}")
@router.post("/webhook")
async def gitlab_webhook(background_tasks: BackgroundTasks, request: Request):
log_context = {"server_type": "gitlab_app"}
if request.headers.get("X-Gitlab-Token") and secret_provider:
request_token = request.headers.get("X-Gitlab-Token")
secret = secret_provider.get_secret(request_token)
try:
secret_dict = json.loads(secret)
gitlab_token = secret_dict["gitlab_token"]
log_context["sender"] = secret_dict.get("token_name", secret_dict.get("id", "unknown"))
context["settings"] = copy.deepcopy(global_settings)
context["settings"].gitlab.personal_access_token = gitlab_token
except Exception as e:
get_logger().error(f"Failed to validate secret {request_token}: {e}")
return JSONResponse(status_code=status.HTTP_401_UNAUTHORIZED, content=jsonable_encoder({"message": "unauthorized"}))
elif get_settings().get("GITLAB.SHARED_SECRET"):
secret = get_settings().get("GITLAB.SHARED_SECRET")
if not request.headers.get("X-Gitlab-Token") == secret:
return JSONResponse(status_code=status.HTTP_401_UNAUTHORIZED, content=jsonable_encoder({"message": "unauthorized"}))
else:
return JSONResponse(status_code=status.HTTP_401_UNAUTHORIZED, content=jsonable_encoder({"message": "unauthorized"}))
gitlab_token = get_settings().get("GITLAB.PERSONAL_ACCESS_TOKEN", None)
if not gitlab_token:
return JSONResponse(status_code=status.HTTP_401_UNAUTHORIZED, content=jsonable_encoder({"message": "unauthorized"}))
data = await request.json()
get_logger().info(json.dumps(data))
if data.get('object_kind') == 'merge_request' and data['object_attributes'].get('action') in ['open', 'reopen']:
get_logger().info(f"A merge request has been opened: {data['object_attributes'].get('title')}")
url = data['object_attributes'].get('url')
await _perform_commands_gitlab("pr_commands", PRAgent(), url, log_context)
# handle_request(background_tasks, url, "/review", log_context)
elif data.get('object_kind') == 'note' and data['event_type'] == 'note':
if 'merge_request' in data:
mr = data['merge_request']
url = mr.get('url')
body = data.get('object_attributes', {}).get('note')
if data.get('object_attributes', {}).get('type') == 'DiffNote' and '/ask' in body:
line_range_ = data['object_attributes']['position']['line_range']
# if line_range_['start']['type'] == 'new':
start_line = line_range_['start']['new_line']
end_line = line_range_['end']['new_line']
# else:
# start_line = line_range_['start']['old_line']
# end_line = line_range_['end']['old_line']
question = body.replace('/ask', '').strip()
path = data['object_attributes']['position']['new_path']
side = 'RIGHT'# if line_range_['start']['type'] == 'new' else 'LEFT'
comment_id = data['object_attributes']["discussion_id"]
get_logger().info(f"Handling line comment")
body = f"/ask_line --line_start={start_line} --line_end={end_line} --side={side} --file_name={path} --comment_id={comment_id} {question}"
handle_request(background_tasks, url, body, log_context)
return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder({"message": "success"}))
@router.get("/")
async def root():
return {"status": "ok"}
def start():
gitlab_url = get_settings().get("GITLAB.URL", None)
if not gitlab_url:
raise ValueError("GITLAB.URL is not set")
get_settings().config.git_provider = "gitlab"
middleware = [Middleware(RawContextMiddleware)]
app = FastAPI(middleware=middleware)
app.include_router(router)
uvicorn.run(app, host="0.0.0.0", port=3000)
if __name__ == '__main__':
start()

354
pr_agent/servers/help.py Normal file
View File

@ -0,0 +1,354 @@
class HelpMessage:
@staticmethod
def get_general_commands_text():
commands_text = "> - **/review**: Request a review of your Pull Request. \n" \
"> - **/describe**: Update the PR title and description based on the contents of the PR. \n" \
"> - **/improve [--extended]**: Suggest code improvements. Extended mode provides a higher quality feedback. \n" \
"> - **/ask \\<QUESTION\\>**: Ask a question about the PR. \n" \
"> - **/update_changelog**: Update the changelog based on the PR's contents. \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" \
">See the [tools guide](https://github.com/Codium-ai/pr-agent/blob/main/docs/TOOLS_GUIDE.md) for more details.\n" \
">To list the possible configuration parameters, add a **/config** comment. \n"
return commands_text
@staticmethod
def get_general_bot_help_text():
output = f"> To invoke the PR-Agent, add a comment using one of the following commands: \n{HelpMessage.get_general_commands_text()} \n"
return output
@staticmethod
def get_review_usage_guide():
output ="**Overview:**\n"
output +="The `review` tool scans the PR code changes, and generates a PR review. The tool can be triggered [automatically](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) every time a new PR is opened, or can be invoked manually by commenting on any PR.\n"
output +="""\
When commenting, to edit [configurations](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L19) related to the review tool (`pr_reviewer` section), use the following template:
```
/review --pr_reviewer.some_config1=... --pr_reviewer.some_config2=...
```
With a [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#working-with-github-app), use the following template:
```
[pr_reviewer]
some_config1=...
some_config2=...
```
"""
output +="\n\n<table>"
# extra instructions
output += "<tr><td><details> <summary><strong> Utilizing extra instructions</strong></summary><hr>\n\n"
output += '''\
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.
Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify the relevant sub-tool, and the relevant aspects of the PR that you want to emphasize.
Examples for extra instructions:
```
[pr_reviewer] # /review #
extra_instructions="""
In the 'possible issues' section, emphasize the following:
- Does the code logic cover relevant edge cases?
- Is the code logic clear and easy to understand?
- Is the code logic efficient?
...
"""
```
Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.
'''
output += "\n\n</details></td></tr>\n\n"
# automation
output += "<tr><td><details> <summary><strong> How to enable\\disable automation</strong></summary><hr>\n\n"
output += """\
- When you first install PR-Agent app, the [default mode](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) for the `review` tool is:
```
pr_commands = ["/review", ...]
```
meaning the `review` tool will run automatically on every PR, with the default configuration.
Edit this field to enable/disable the tool, or to change the used configurations
"""
output += "\n\n</details></td></tr>\n\n"
# # code feedback
# output += "<tr><td><details> <summary><strong> About the 'Code feedback' section</strong></summary><hr>\n\n"
# output+="""\
# The `review` tool provides several type of feedbacks, one of them is code suggestions.
# If you are interested **only** in the code suggestions, it is recommended to use the [`improve`](https://github.com/Codium-ai/pr-agent/blob/main/docs/IMPROVE.md) feature instead, since it dedicated only to code suggestions, and usually gives better results.
# Use the `review` tool if you want to get a more comprehensive feedback, which includes code suggestions as well.
# """
# output += "\n\n</details></td></tr>\n\n"
# auto-labels
output += "<tr><td><details> <summary><strong> Auto-labels</strong></summary><hr>\n\n"
output+="""\
The `review` tool can auto-generate two specific types of labels for a PR:
- a `possible security issue` label, that detects possible [security issues](https://github.com/Codium-ai/pr-agent/blob/tr/user_description/pr_agent/settings/pr_reviewer_prompts.toml#L136) (`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)
"""
output += "\n\n</details></td></tr>\n\n"
# extra sub-tools
output += "<tr><td><details> <summary><strong> Extra sub-tools</strong></summary><hr>\n\n"
output += """\
The `review` tool provides a collection of possible feedbacks about a PR.
It is recommended to review the [possible options](https://github.com/Codium-ai/pr-agent/blob/main/docs/REVIEW.md#enabledisable-features), and choose the ones relevant for your use case.
Some of the feature that are disabled by default are quite useful, and should be considered for enabling. For example:
`require_score_review`, `require_soc2_ticket`, and more.
"""
output += "\n\n</details></td></tr>\n\n"
output += "<tr><td><details> <summary><strong> Auto-approve PRs</strong></summary><hr>\n\n"
output += '''\
By invoking:
```
/review auto_approve
```
The tool will automatically approve the PR, and add a comment with the approval.
To ensure safety, the auto-approval feature is disabled by default. To enable auto-approval, you need to actively set in a pre-defined configuration file the following:
```
[pr_reviewer]
enable_auto_approval = true
```
(this specific flag cannot be set with a command line argument, only in the configuration file, committed to the repository)
You can also enable auto-approval only if the PR meets certain requirements, such as that the `estimated_review_effort` is equal or below a certain threshold, by adjusting the flag:
```
[pr_reviewer]
maximal_review_effort = 5
```
'''
output += "\n\n</details></td></tr>\n\n"
# general
output += "\n\n<tr><td><details> <summary><strong> More PR-Agent commands</strong></summary><hr> \n\n"
output += HelpMessage.get_general_bot_help_text()
output += "\n\n</details></td></tr>\n\n"
output += "</table>"
output += f"\n\nSee the [review usage](https://github.com/Codium-ai/pr-agent/blob/main/docs/REVIEW.md) page for a comprehensive guide on using this tool.\n\n"
return output
@staticmethod
def get_describe_usage_guide():
output = "**Overview:**\n"
output += "The `describe` tool scans the PR code changes, and generates a description for the PR - title, type, summary, walkthrough and labels. "
output += "The tool can be triggered [automatically](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) every time a new PR is opened, or can be invoked manually by commenting on a PR.\n"
output += """\
When commenting, to edit [configurations](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L46) related to the describe tool (`pr_description` section), use the following template:
```
/describe --pr_description.some_config1=... --pr_description.some_config2=...
```
With a [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#working-with-github-app), use the following template:
```
[pr_description]
some_config1=...
some_config2=...
```
"""
output += "\n\n<table>"
# automation
output += "<tr><td><details> <summary><strong> Enabling\\disabling automation </strong></summary><hr>\n\n"
output += """\
- When you first install the app, the [default mode](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) for the describe tool is:
```
pr_commands = ["/describe --pr_description.add_original_user_description=true"
"--pr_description.keep_original_user_title=true", ...]
```
meaning the `describe` tool will run automatically on every PR, will keep the original title, and will add the original user description above the generated description.
- 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.
- `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.
"""
output += "\n\n</details></td></tr>\n\n"
# custom labels
output += "<tr><td><details> <summary><strong> Custom labels </strong></summary><hr>\n\n"
output += """\
The default labels of the `describe` tool are quite generic: [`Bug fix`, `Tests`, `Enhancement`, `Documentation`, `Other`].
If you specify [custom labels](https://github.com/Codium-ai/pr-agent/blob/main/docs/DESCRIBE.md#handle-custom-labels-from-the-repos-labels-page-gem) in the repo's labels page or via configuration file, you can get tailored labels for your use cases.
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
- ...
The list above is eclectic, and aims to give an idea of different possibilities. Define custom labels that are relevant for your repo and use cases.
Note that Labels are not mutually exclusive, so you can add multiple label categories.
Make sure to provide proper title, and a detailed and well-phrased description for each label, so the tool will know when to suggest it.
"""
output += "\n\n</details></td></tr>\n\n"
# Inline File Walkthrough
output += "<tr><td><details> <summary><strong> Inline File Walkthrough 💎</strong></summary><hr>\n\n"
output += """\
For enhanced user experience, the `describe` tool can add file summaries directly to the "Files changed" tab in the PR page.
This will enable you to quickly understand the changes in each file, while reviewing the code changes (diffs).
To enable inline file summary, set `pr_description.inline_file_summary` in the configuration file, possible values are:
- `'table'`: File changes walkthrough table will be displayed on the top of the "Files changed" tab, in addition to the "Conversation" tab.
- `true`: A collapsable file comment with changes title and a changes summary for each file in the PR.
- `false` (default): File changes walkthrough will be added only to the "Conversation" tab.
"""
# extra instructions
output += "<tr><td><details> <summary><strong> Utilizing extra instructions</strong></summary><hr>\n\n"
output += '''\
The `describe` tool can be configured with extra instructions, to guide the model to a feedback tailored to the needs of your project.
Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Notice that the general structure of the description is fixed, and cannot be changed. Extra instructions can change the content or style of each sub-section of the PR description.
Examples for extra instructions:
```
[pr_description]
extra_instructions="""
- The PR title should be in the format: '<PR type>: <title>'
- The title should be short and concise (up to 10 words)
- ...
"""
```
Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.
'''
output += "\n\n</details></td></tr>\n\n"
# general
output += "\n\n<tr><td><details> <summary><strong> More PR-Agent commands</strong></summary><hr> \n\n"
output += HelpMessage.get_general_bot_help_text()
output += "\n\n</details></td></tr>\n\n"
output += "</table>"
output += f"\n\nSee the [describe usage](https://github.com/Codium-ai/pr-agent/blob/main/docs/DESCRIBE.md) page for a comprehensive guide on using this tool.\n\n"
return output
@staticmethod
def get_ask_usage_guide():
output = "**Overview:**\n"
output += """\
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 "..."
```
Note that the tool does not have "memory" of previous questions, and answers each question independently.
"""
output += "\n\n<table>"
# general
output += "\n\n<tr><td><details> <summary><strong> More PR-Agent commands</strong></summary><hr> \n\n"
output += HelpMessage.get_general_bot_help_text()
output += "\n\n</details></td></tr>\n\n"
output += "</table>"
output += f"\n\nSee the [ask usage](https://github.com/Codium-ai/pr-agent/blob/main/docs/ASK.md) page for a comprehensive guide on using this tool.\n\n"
return output
@staticmethod
def get_improve_usage_guide():
output = "**Overview:**\n"
output += "The `improve` tool scans the PR code changes, and automatically generates suggestions for improving the PR code. "
output += "The tool can be triggered [automatically](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) every time a new PR is opened, or can be invoked manually by commenting on a PR.\n"
output += """\
When commenting, to edit [configurations](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L69) related to the improve tool (`pr_code_suggestions` section), use the following template:
```
/improve --pr_code_suggestions.some_config1=... --pr_code_suggestions.some_config2=...
```
With a [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#working-with-github-app), use the following template:
```
[pr_code_suggestions]
some_config1=...
some_config2=...
```
"""
output += "\n\n<table>"
# automation
output += "<tr><td><details> <summary><strong> Enabling\\disabling automation </strong></summary><hr>\n\n"
output += """\
When you first install the app, the [default mode](https://github.com/Codium-ai/pr-agent/blob/main/Usage.md#github-app-automatic-tools) for the improve tool is:
```
pr_commands = ["/improve --pr_code_suggestions.summarize=true", ...]
```
meaning the `improve` tool will run automatically on every PR, with summarization enabled. Delete this line to disable the tool from running automatically.
"""
output += "\n\n</details></td></tr>\n\n"
# extra instructions
output += "<tr><td><details> <summary><strong> Utilizing extra instructions</strong></summary><hr>\n\n"
output += '''\
Extra instructions are very important for the `improve` tool, since they enable to guide the model to suggestions that are more relevant to the specific needs of the project.
Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify relevant aspects that you want the model to focus on.
Examples for extra instructions:
```
[pr_code_suggestions] # /improve #
extra_instructions="""
Emphasize the following aspects:
- Does the code logic cover relevant edge cases?
- Is the code logic clear and easy to understand?
- Is the code logic efficient?
...
"""
```
Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.
'''
output += "\n\n</details></td></tr>\n\n"
# suggestions quality
output += "\n\n<tr><td><details> <summary><strong> A note on code suggestions quality</strong></summary><hr> \n\n"
output += """\
- While the current AI for code is getting better and better (GPT-4), it's not flawless. Not all the suggestions will be perfect, and a user should not accept all of them automatically.
- Suggestions are not meant to be simplistic. Instead, they aim to give deep feedback and raise questions, ideas and thoughts to the user, who can then use his judgment, experience, and understanding of the code base.
- Recommended to use the 'extra_instructions' field to guide the model to suggestions that are more relevant to the specific needs of the project, or use the [custom suggestions :gem:](https://github.com/Codium-ai/pr-agent/blob/main/docs/CUSTOM_SUGGESTIONS.md) tool
- With large PRs, best quality will be obtained by using 'improve --extended' mode.
"""
output += "\n\n</details></td></tr>\n\n"\
# general
output += "\n\n<tr><td><details> <summary><strong> More PR-Agent commands</strong></summary><hr> \n\n"
output += HelpMessage.get_general_bot_help_text()
output += "\n\n</details></td></tr>\n\n"
output += "</table>"
output += f"\n\nSee the [improve usage](https://github.com/Codium-ai/pr-agent/blob/main/docs/IMPROVE.md) page for a more comprehensive guide on using this tool.\n\n"
return output

View File

@ -0,0 +1,17 @@
from fastapi import FastAPI
from mangum import Mangum
from starlette.middleware import Middleware
from starlette_context.middleware import RawContextMiddleware
from pr_agent.servers.github_app import router
middleware = [Middleware(RawContextMiddleware)]
app = FastAPI(middleware=middleware)
app.include_router(router)
handler = Mangum(app, lifespan="off")
def serverless(event, context):
return handler(event, context)

View File

@ -1,5 +1,8 @@
import hashlib
import hmac
import time
from collections import defaultdict
from typing import Callable, Any
from fastapi import HTTPException
@ -21,3 +24,63 @@ def verify_signature(payload_body, secret_token, signature_header):
if not hmac.compare_digest(expected_signature, signature_header):
raise HTTPException(status_code=403, detail="Request signatures didn't match!")
class RateLimitExceeded(Exception):
"""Raised when the git provider API rate limit has been exceeded."""
pass
class DefaultDictWithTimeout(defaultdict):
"""A defaultdict with a time-to-live (TTL)."""
def __init__(
self,
default_factory: Callable[[], Any] = None,
ttl: int = None,
refresh_interval: int = 60,
update_key_time_on_get: bool = True,
*args,
**kwargs,
):
"""
Args:
default_factory: The default factory to use for keys that are not in the dictionary.
ttl: The time-to-live (TTL) in seconds.
refresh_interval: How often to refresh the dict and delete items older than the TTL.
update_key_time_on_get: Whether to update the access time of a key also on get (or only when set).
"""
super().__init__(default_factory, *args, **kwargs)
self.__key_times = dict()
self.__ttl = ttl
self.__refresh_interval = refresh_interval
self.__update_key_time_on_get = update_key_time_on_get
self.__last_refresh = self.__time() - self.__refresh_interval
@staticmethod
def __time():
return time.monotonic()
def __refresh(self):
if self.__ttl is None:
return
request_time = self.__time()
if request_time - self.__last_refresh > self.__refresh_interval:
return
to_delete = [key for key, key_time in self.__key_times.items() if request_time - key_time > self.__ttl]
for key in to_delete:
del self[key]
self.__last_refresh = request_time
def __getitem__(self, __key):
if self.__update_key_time_on_get:
self.__key_times[__key] = self.__time()
self.__refresh()
return super().__getitem__(__key)
def __setitem__(self, __key, __value):
self.__key_times[__key] = self.__time()
return super().__setitem__(__key, __value)
def __delitem__(self, __key):
del self.__key_times[__key]
return super().__delitem__(__key)

View File

@ -7,17 +7,46 @@
# See README for details about GitHub App deployment.
[openai]
key = "<API_KEY>" # Acquire through https://platform.openai.com
org = "<ORGANIZATION>" # Optional, may be commented out.
key = "" # Acquire through https://platform.openai.com
#org = "<ORGANIZATION>" # 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 = "<API_BASE>" # The base URL for your Azure OpenAI resource. e.g. "https://<your resource name>.openai.azure.com"
#deployment_id = "<DEPLOYMENT_ID>" # The deployment name you chose when you deployed the engine
#api_base = "" # The base URL for your Azure OpenAI resource. e.g. "https://<your resource name>.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
[pinecone]
api_key = "..."
environment = "gcp-starter"
[anthropic]
key = "" # Optional, uncomment if you want to use Anthropic. Acquire through https://www.anthropic.com/
[cohere]
key = "" # Optional, uncomment if you want to use Cohere. Acquire through https://dashboard.cohere.ai/
[replicate]
key = "" # Optional, uncomment if you want to use Replicate. Acquire through https://replicate.com/
[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
[ollama]
api_base = "" # the base url for your local Llama 2, Code Llama, and other models inference endpoint. Acquire through https://ollama.ai/
[vertexai]
vertex_project = "" # the google cloud platform project name for your vertexai deployment
vertex_location = "" # the google cloud platform location for your vertexai deployment
[aws]
bedrock_region = "" # the AWS region to call Bedrock APIs
[github]
# ---- Set the following only for deployment type == "user"
user_token = "<TOKEN>" # A GitHub personal access token with 'repo' scope.
user_token = "" # A GitHub personal access token with 'repo' scope.
deployment_type = "user" #set to user by default
# ---- Set the following only for deployment type == "app", see README for details.
private_key = """\
@ -32,3 +61,29 @@ webhook_secret = "<WEBHOOK SECRET>" # Optional, may be commented out.
# Gitlab personal access token
personal_access_token = ""
[bitbucket]
# For Bitbucket personal/repository bearer token
bearer_token = ""
[bitbucket_server]
# For Bitbucket Server bearer token
auth_token = ""
webhook_secret = ""
# For Bitbucket app
app_key = ""
base_url = ""
[litellm]
LITELLM_TOKEN = "" # see https://docs.litellm.ai/docs/debugging/hosted_debugging for details and instructions on how to get a token
[azure_devops]
# For Azure devops personal access token
org = ""
pat = ""
[azure_devops_server]
# For Azure devops Server basic auth - configured in the webhook creation
# Optional, uncomment if you want to use Azure devops webhooks. Value assinged when you create the webhook
# webhook_username = "<basic auth user>"
# webhook_password = "<basic auth password>"

View File

@ -1,31 +1,207 @@
[config]
model="gpt-4-0613"
model="gpt-4" # "gpt-4-0125-preview"
model_turbo="gpt-4-0125-preview"
fallback_models=["gpt-3.5-turbo-16k"]
git_provider="github"
publish_review=true
verbosity_level=0 # 0,1,2
publish_output=true
publish_output_progress=true
verbosity_level=0 # 0,1,2
use_extra_bad_extensions=false
use_wiki_settings_file=true
use_repo_settings_file=true
use_global_settings_file=true
ai_timeout=120 # 2minutes
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.
patch_extra_lines = 1
secret_provider="google_cloud_storage"
cli_mode=false
ai_disclaimer_title="" # Pro feature, title for a collapsible disclaimer to AI outputs
ai_disclaimer="" # Pro feature, full text for the AI disclaimer
[pr_reviewer]
require_focused_review=true
[pr_reviewer] # /review #
# enable/disable features
require_focused_review=false
require_score_review=false
require_tests_review=true
require_security_review=true
extended_code_suggestions=false
require_estimate_effort_to_review=true
# soc2
require_soc2_ticket=false
soc2_ticket_prompt="Does the PR description include a link to ticket in a project management system (e.g., Jira, Asana, Trello, etc.) ?"
# general options
num_code_suggestions=4
inline_code_comments = false
ask_and_reflect=false
#automatic_review=true
persistent_comment=true
extra_instructions = ""
# review labels
enable_review_labels_security=true
enable_review_labels_effort=true
# specific configurations for incremental review (/review -i)
require_all_thresholds_for_incremental_review=false
minimal_commits_for_incremental_review=0
minimal_minutes_for_incremental_review=0
enable_help_text=true # Determines whether to include help text in the PR review. Enabled by default.
# auto approval
enable_auto_approval=false
maximal_review_effort=5
[pr_questions]
[pr_description] # /describe #
publish_labels=true
publish_description_as_comment=false
add_original_user_description=true
keep_original_user_title=true
use_bullet_points=true
extra_instructions = ""
enable_pr_type=true
final_update_message = true
enable_help_text=false
enable_help_comment=true
## changes walkthrough section
enable_semantic_files_types=true
collapsible_file_list='adaptive' # true, false, 'adaptive'
inline_file_summary=false # false, true, 'table'
# markers
use_description_markers=false
include_generated_by_header=true
#custom_labels = ['Bug fix', 'Tests', 'Bug fix with tests', 'Enhancement', 'Documentation', 'Other']
[pr_questions] # /ask #
enable_help_text=true
[pr_code_suggestions] # /improve #
max_context_tokens=8000
num_code_suggestions=4
summarize = true
extra_instructions = ""
rank_suggestions = false
enable_help_text=true
# params for '/improve --extended' mode
auto_extended_mode=true
num_code_suggestions_per_chunk=5
max_number_of_calls = 3
parallel_calls = true
rank_extended_suggestions = false
final_clip_factor = 0.8
[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 = ""
[pr_analyze] # /analyze #
[pr_test] # /test #
extra_instructions = ""
testing_framework = "" # specify the testing framework you want to use
num_tests=3 # number of tests to generate. max 5.
avoid_mocks=true # if true, the generated tests will prefer to use real objects instead of mocks
file = "" # in case there are several components with the same name, you can specify the relevant file
class_name = "" # in case there are several methods with the same name in the same file, you can specify the relevant class name
enable_help_text=true
[checks] # /checks (pro feature) #
enable_auto_checks_feedback=true
excluded_checks_list=["lint"] # list of checks to exclude, for example: ["check1", "check2"]
persistent_comment=true
enable_help_text=true
[pr_help] # /help #
[pr_config] # /config #
[github]
# The type of deployment to create. Valid values are 'app' or 'user'.
deployment_type = "user"
ratelimit_retries = 5
base_url = "https://api.github.com"
publish_inline_comments_fallback_with_verification = true
try_fix_invalid_inline_comments = true
[github_action_config]
# 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
override_deployment_type = true
# settings for "pull_request" event
handle_pr_actions = ['opened', 'reopened', 'ready_for_review']
pr_commands = [
"/describe --pr_description.add_original_user_description=true --pr_description.keep_original_user_title=true",
"/review --pr_reviewer.num_code_suggestions=0",
"/improve --pr_code_suggestions.summarize=true",
]
# settings for "pull_request" event with "synchronize" action - used to detect and handle push triggers for new commits
handle_push_trigger = false
push_trigger_ignore_bot_commits = true
push_trigger_ignore_merge_commits = true
push_trigger_wait_for_initial_review = true
push_trigger_pending_tasks_backlog = true
push_trigger_pending_tasks_ttl = 300
push_commands = [
"/describe --pr_description.add_original_user_description=true --pr_description.keep_original_user_title=true",
"/review --pr_reviewer.num_code_suggestions=0",
]
ignore_pr_title = []
[gitlab]
# URL to the gitlab service
gitlab_url = "https://gitlab.com"
url = "https://gitlab.com" # URL to the gitlab service
pr_commands = [
"/describe --pr_description.add_original_user_description=true --pr_description.keep_original_user_title=true",
"/review --pr_reviewer.num_code_suggestions=0",
"/improve --pr_code_suggestions.summarize=true",
]
# Polling (either project id or namespace/project_name) syntax can be used
projects_to_monitor = ['org_name/repo_name']
[bitbucket_app]
#auto_review = true # set as config var in .pr_agent.toml
#auto_describe = true # set as config var in .pr_agent.toml
#auto_improve = true # set as config var in .pr_agent.toml
# Polling trigger
magic_word = "AutoReview"
# Polling interval
polling_interval_seconds = 30
[local]
# LocalGitProvider settings - uncomment to use paths other than default
# description_path= "path/to/description.md"
# review_path= "path/to/review.md"
[gerrit]
# endpoint to the gerrit service
# url = "ssh://gerrit.example.com:29418"
# user for gerrit authentication
# user = "ai-reviewer"
# patch server where patches will be saved
# patch_server_endpoint = "http://127.0.0.1:5000/patch"
# token to authenticate in the patch server
# patch_server_token = ""
[bitbucket_server]
# URL to the BitBucket Server instance
# url = "https://git.bitbucket.com"
url = ""
[litellm]
#use_client = false
[pr_similar_issue]
skip_comments = false
force_update_dataset = false
max_issues_to_scan = 500
vectordb = "pinecone"
[pinecone]
# fill and place in .secrets.toml
#api_key = ...
# environment = "gcp-starter"
[lancedb]
uri = "./lancedb"

View File

@ -0,0 +1,16 @@
[config]
enable_custom_labels=false
## template for custom labels
#[custom_labels."Bug fix"]
#description = """Fixes a bug in the code"""
#[custom_labels."Tests"]
#description = """Adds or modifies tests"""
#[custom_labels."Bug fix with tests"]
#description = """Fixes a bug in the code and adds or modifies tests"""
#[custom_labels."Enhancement"]
#description = """Adds new features or modifies existing ones"""
#[custom_labels."Documentation"]
#description = """Adds or modifies documentation"""
#[custom_labels."Other"]
#description = """Other changes that do not fit in any of the above categories"""

View File

@ -0,0 +1,11 @@
[ignore]
glob = [
# Ignore files and directories matching these glob patterns.
# See https://docs.python.org/3/library/glob.html
'vendor/**',
]
regex = [
# Ignore files and directories matching these regex patterns.
# See https://learnbyexample.github.io/python-regex-cheatsheet/
]

View File

@ -0,0 +1,438 @@
[bad_extensions]
default = [
'app',
'bin',
'bmp',
'bz2',
'class',
'csv',
'dat',
'db',
'dll',
'dylib',
'egg',
'eot',
'exe',
'gif',
'gitignore',
'glif',
'gradle',
'gz',
'ico',
'jar',
'jpeg',
'jpg',
'lo',
'lock',
'log',
'mp3',
'mp4',
'nar',
'o',
'ogg',
'otf',
'p',
'pdf',
'png',
'pickle',
'pkl',
'pyc',
'pyd',
'pyo',
'rkt',
'so',
'ss',
'svg',
'tar',
'tsv',
'ttf',
'war',
'webm',
'woff',
'woff2',
'xz',
'zip',
'zst',
'snap',
'lockb'
]
extra = [
'md',
'txt'
]
[language_extension_map_org]
ABAP = [".abap", ]
"AGS Script" = [".ash", ]
AMPL = [".ampl", ]
ANTLR = [".g4", ]
"API Blueprint" = [".apib", ]
APL = [".apl", ".dyalog", ]
ASP = [".asp", ".asax", ".ascx", ".ashx", ".asmx", ".aspx", ".axd", ]
ATS = [".dats", ".hats", ".sats", ]
ActionScript = [".as", ]
Ada = [".adb", ".ada", ".ads", ]
Agda = [".agda", ]
Alloy = [".als", ]
ApacheConf = [".apacheconf", ".vhost", ]
AppleScript = [".applescript", ".scpt", ]
Arc = [".arc", ]
Arduino = [".ino", ]
AsciiDoc = [".asciidoc", ".adoc", ]
AspectJ = [".aj", ]
Assembly = [".asm", ".a51", ".nasm", ]
Augeas = [".aug", ]
AutoHotkey = [".ahk", ".ahkl", ]
AutoIt = [".au3", ]
Awk = [".awk", ".auk", ".gawk", ".mawk", ".nawk", ]
Batchfile = [".bat", ".cmd", ]
Befunge = [".befunge", ]
Bison = [".bison", ]
BitBake = [".bb", ]
BlitzBasic = [".decls", ]
BlitzMax = [".bmx", ]
Bluespec = [".bsv", ]
Boo = [".boo", ]
Brainfuck = [".bf", ]
Brightscript = [".brs", ]
Bro = [".bro", ]
C = [".c", ".cats", ".h", ".idc", ".w", ]
"C#" = [".cs", ".cake", ".cshtml", ".csx", ]
"C++" = [".cpp", ".c++", ".cc", ".cp", ".cxx", ".h++", ".hh", ".hpp", ".hxx", ".inl", ".ipp", ".tcc", ".tpp", ".C", ".H", ]
C-ObjDump = [".c-objdump", ]
"C2hs Haskell" = [".chs", ]
CLIPS = [".clp", ]
CMake = [".cmake", ".cmake.in", ]
COBOL = [".cob", ".cbl", ".ccp", ".cobol", ".cpy", ]
CSS = [".css", ]
CSV = [".csv", ]
"Cap'n Proto" = [".capnp", ]
CartoCSS = [".mss", ]
Ceylon = [".ceylon", ]
Chapel = [".chpl", ]
ChucK = [".ck", ]
Cirru = [".cirru", ]
Clarion = [".clw", ]
Clean = [".icl", ".dcl", ]
Click = [".click", ]
Clojure = [".clj", ".boot", ".cl2", ".cljc", ".cljs", ".cljs.hl", ".cljscm", ".cljx", ".hic", ]
CoffeeScript = [".coffee", "._coffee", ".cjsx", ".cson", ".iced", ]
ColdFusion = [".cfm", ".cfml", ]
"ColdFusion CFC" = [".cfc", ]
"Common Lisp" = [".lisp", ".asd", ".lsp", ".ny", ".podsl", ".sexp", ]
"Component Pascal" = [".cps", ]
Coq = [".coq", ]
Cpp-ObjDump = [".cppobjdump", ".c++-objdump", ".c++objdump", ".cpp-objdump", ".cxx-objdump", ]
Creole = [".creole", ]
Crystal = [".cr", ]
Csound = [".csd", ]
Cucumber = [".feature", ]
Cuda = [".cu", ".cuh", ]
Cycript = [".cy", ]
Cython = [".pyx", ".pxd", ".pxi", ]
D = [".di", ]
D-ObjDump = [".d-objdump", ]
"DIGITAL Command Language" = [".com", ]
DM = [".dm", ]
"DNS Zone" = [".zone", ".arpa", ]
"Darcs Patch" = [".darcspatch", ".dpatch", ]
Dart = [".dart", ]
Diff = [".diff", ".patch", ]
Dockerfile = [".dockerfile", "Dockerfile", ]
Dogescript = [".djs", ]
Dylan = [".dylan", ".dyl", ".intr", ".lid", ]
E = [".E", ]
ECL = [".ecl", ".eclxml", ]
Eagle = [".sch", ".brd", ]
"Ecere Projects" = [".epj", ]
Eiffel = [".e", ]
Elixir = [".ex", ".exs", ]
Elm = [".elm", ]
"Emacs Lisp" = [".el", ".emacs", ".emacs.desktop", ]
EmberScript = [".em", ".emberscript", ]
Erlang = [".erl", ".escript", ".hrl", ".xrl", ".yrl", ]
"F#" = [".fs", ".fsi", ".fsx", ]
FLUX = [".flux", ]
FORTRAN = [".f90", ".f", ".f03", ".f08", ".f77", ".f95", ".for", ".fpp", ]
Factor = [".factor", ]
Fancy = [".fy", ".fancypack", ]
Fantom = [".fan", ]
Formatted = [".eam.fs", ]
Forth = [".fth", ".4th", ".forth", ".frt", ]
FreeMarker = [".ftl", ]
G-code = [".g", ".gco", ".gcode", ]
GAMS = [".gms", ]
GAP = [".gap", ".gi", ]
GAS = [".s", ]
GDScript = [".gd", ]
GLSL = [".glsl", ".fp", ".frag", ".frg", ".fsh", ".fshader", ".geo", ".geom", ".glslv", ".gshader", ".shader", ".vert", ".vrx", ".vsh", ".vshader", ]
Genshi = [".kid", ]
"Gentoo Ebuild" = [".ebuild", ]
"Gentoo Eclass" = [".eclass", ]
"Gettext Catalog" = [".po", ".pot", ]
Glyph = [".glf", ]
Gnuplot = [".gp", ".gnu", ".gnuplot", ".plot", ".plt", ]
Go = [".go", ]
Golo = [".golo", ]
Gosu = [".gst", ".gsx", ".vark", ]
Grace = [".grace", ]
Gradle = [".gradle", ]
"Grammatical Framework" = [".gf", ]
GraphQL = [".graphql", ]
"Graphviz (DOT)" = [".dot", ".gv", ]
Groff = [".man", ".1", ".1in", ".1m", ".1x", ".2", ".3", ".3in", ".3m", ".3qt", ".3x", ".4", ".5", ".6", ".7", ".8", ".9", ".me", ".rno", ".roff", ]
Groovy = [".groovy", ".grt", ".gtpl", ".gvy", ]
"Groovy Server Pages" = [".gsp", ]
HCL = [".hcl", ".tf", ]
HLSL = [".hlsl", ".fxh", ".hlsli", ]
HTML = [".html", ".htm", ".html.hl", ".xht", ".xhtml", ]
"HTML+Django" = [".mustache", ".jinja", ]
"HTML+EEX" = [".eex", ]
"HTML+ERB" = [".erb", ".erb.deface", ]
"HTML+PHP" = [".phtml", ]
HTTP = [".http", ]
Haml = [".haml", ".haml.deface", ]
Handlebars = [".handlebars", ".hbs", ]
Harbour = [".hb", ]
Haskell = [".hs", ".hsc", ]
Haxe = [".hx", ".hxsl", ]
Hy = [".hy", ]
IDL = [".dlm", ]
"IGOR Pro" = [".ipf", ]
INI = [".ini", ".cfg", ".prefs", ".properties", ]
"IRC log" = [".irclog", ".weechatlog", ]
Idris = [".idr", ".lidr", ]
"Inform 7" = [".ni", ".i7x", ]
"Inno Setup" = [".iss", ]
Io = [".io", ]
Ioke = [".ik", ]
Isabelle = [".thy", ]
J = [".ijs", ]
JFlex = [".flex", ".jflex", ]
JSON = [".json", ".geojson", ".lock", ".topojson", ]
JSON5 = [".json5", ]
JSONLD = [".jsonld", ]
JSONiq = [".jq", ]
JSX = [".jsx", ]
Jade = [".jade", ]
Jasmin = [".j", ]
Java = [".java", ]
"Java Server Pages" = [".jsp", ]
JavaScript = [".js", "._js", ".bones", ".es6", ".jake", ".jsb", ".jscad", ".jsfl", ".jsm", ".jss", ".njs", ".pac", ".sjs", ".ssjs", ".xsjs", ".xsjslib", ]
Julia = [".jl", ]
"Jupyter Notebook" = [".ipynb", ]
KRL = [".krl", ]
KiCad = [".kicad_pcb", ]
Kit = [".kit", ]
Kotlin = [".kt", ".ktm", ".kts", ]
LFE = [".lfe", ]
LLVM = [".ll", ]
LOLCODE = [".lol", ]
LSL = [".lsl", ".lslp", ]
LabVIEW = [".lvproj", ]
Lasso = [".lasso", ".las", ".lasso8", ".lasso9", ".ldml", ]
Latte = [".latte", ]
Lean = [".lean", ".hlean", ]
Less = [".less", ]
Lex = [".lex", ]
LilyPond = [".ly", ".ily", ]
"Linker Script" = [".ld", ".lds", ]
Liquid = [".liquid", ]
"Literate Agda" = [".lagda", ]
"Literate CoffeeScript" = [".litcoffee", ]
"Literate Haskell" = [".lhs", ]
LiveScript = [".ls", "._ls", ]
Logos = [".xm", ".x", ".xi", ]
Logtalk = [".lgt", ".logtalk", ]
LookML = [".lookml", ]
Lua = [".lua", ".nse", ".pd_lua", ".rbxs", ".wlua", ]
M = [".mumps", ]
M4 = [".m4", ]
MAXScript = [".mcr", ]
MTML = [".mtml", ]
MUF = [".muf", ]
Makefile = [".mak", ".mk", ".mkfile", "Makefile", ]
Mako = [".mako", ".mao", ]
Maple = [".mpl", ]
Markdown = [".md", ".markdown", ".mkd", ".mkdn", ".mkdown", ".ron", ]
Mask = [".mask", ]
Mathematica = [".mathematica", ".cdf", ".ma", ".mt", ".nb", ".nbp", ".wl", ".wlt", ]
Matlab = [".matlab", ]
Max = [".maxpat", ".maxhelp", ".maxproj", ".mxt", ".pat", ]
MediaWiki = [".mediawiki", ".wiki", ]
Metal = [".metal", ]
MiniD = [".minid", ]
Mirah = [".druby", ".duby", ".mir", ".mirah", ]
Modelica = [".mo", ]
"Module Management System" = [".mms", ".mmk", ]
Monkey = [".monkey", ]
MoonScript = [".moon", ]
Myghty = [".myt", ]
NSIS = [".nsi", ".nsh", ]
NetLinx = [".axs", ".axi", ]
"NetLinx+ERB" = [".axs.erb", ".axi.erb", ]
NetLogo = [".nlogo", ]
Nginx = [".nginxconf", ]
Nimrod = [".nim", ".nimrod", ]
Ninja = [".ninja", ]
Nit = [".nit", ]
Nix = [".nix", ]
Nu = [".nu", ]
NumPy = [".numpy", ".numpyw", ".numsc", ]
OCaml = [".ml", ".eliom", ".eliomi", ".ml4", ".mli", ".mll", ".mly", ]
ObjDump = [".objdump", ]
"Objective-C++" = [".mm", ]
Objective-J = [".sj", ]
Octave = [".oct", ]
Omgrofl = [".omgrofl", ]
Opa = [".opa", ]
Opal = [".opal", ]
OpenCL = [".cl", ".opencl", ]
"OpenEdge ABL" = [".p", ]
OpenSCAD = [".scad", ]
Org = [".org", ]
Ox = [".ox", ".oxh", ".oxo", ]
Oxygene = [".oxygene", ]
Oz = [".oz", ]
PAWN = [".pwn", ]
PHP = [".php", ".aw", ".ctp", ".php3", ".php4", ".php5", ".phps", ".phpt", ]
"POV-Ray SDL" = [".pov", ]
Pan = [".pan", ]
Papyrus = [".psc", ]
Parrot = [".parrot", ]
"Parrot Assembly" = [".pasm", ]
"Parrot Internal Representation" = [".pir", ]
Pascal = [".pas", ".dfm", ".dpr", ".lpr", ]
Perl = [".pl", ".al", ".perl", ".ph", ".plx", ".pm", ".psgi", ".t", ]
Perl6 = [".6pl", ".6pm", ".nqp", ".p6", ".p6l", ".p6m", ".pl6", ".pm6", ]
Pickle = [".pkl", ]
PigLatin = [".pig", ]
Pike = [".pike", ".pmod", ]
Pod = [".pod", ]
PogoScript = [".pogo", ]
Pony = [".pony", ]
PostScript = [".ps", ".eps", ]
PowerShell = [".ps1", ".psd1", ".psm1", ]
Processing = [".pde", ]
Prolog = [".prolog", ".yap", ]
"Propeller Spin" = [".spin", ]
"Protocol Buffer" = [".proto", ]
"Public Key" = [".pub", ]
"Pure Data" = [".pd", ]
PureBasic = [".pb", ".pbi", ]
PureScript = [".purs", ]
Python = [".py", ".bzl", ".gyp", ".lmi", ".pyde", ".pyp", ".pyt", ".pyw", ".tac", ".wsgi", ".xpy", ]
"Python traceback" = [".pytb", ]
QML = [".qml", ".qbs", ]
QMake = [".pri", ]
R = [".r", ".rd", ".rsx", ]
RAML = [".raml", ]
RDoc = [".rdoc", ]
REALbasic = [".rbbas", ".rbfrm", ".rbmnu", ".rbres", ".rbtbar", ".rbuistate", ]
RHTML = [".rhtml", ]
RMarkdown = [".rmd", ]
Racket = [".rkt", ".rktd", ".rktl", ".scrbl", ]
"Ragel in Ruby Host" = [".rl", ]
"Raw token data" = [".raw", ]
Rebol = [".reb", ".r2", ".r3", ".rebol", ]
Red = [".red", ".reds", ]
Redcode = [".cw", ]
"Ren'Py" = [".rpy", ]
RenderScript = [".rsh", ]
RobotFramework = [".robot", ]
Rouge = [".rg", ]
Ruby = [".rb", ".builder", ".gemspec", ".god", ".irbrc", ".jbuilder", ".mspec", ".podspec", ".rabl", ".rake", ".rbuild", ".rbw", ".rbx", ".ru", ".ruby", ".thor", ".watchr", ]
Rust = [".rs", ".rs.in", ]
SAS = [".sas", ]
SCSS = [".scss", ]
SMT = [".smt2", ".smt", ]
SPARQL = [".sparql", ".rq", ]
SQF = [".sqf", ".hqf", ]
SQL = [".pls", ".pck", ".pkb", ".pks", ".plb", ".plsql", ".sql", ".cql", ".ddl", ".prc", ".tab", ".udf", ".viw", ".db2", ]
STON = [".ston", ]
SVG = [".svg", ]
Sage = [".sage", ".sagews", ]
SaltStack = [".sls", ]
Sass = [".sass", ]
Scala = [".scala", ".sbt", ]
Scaml = [".scaml", ]
Scheme = [".scm", ".sld", ".sps", ".ss", ]
Scilab = [".sci", ".sce", ]
Self = [".self", ]
Shell = [".sh", ".bash", ".bats", ".command", ".ksh", ".sh.in", ".tmux", ".tool", ".zsh", ]
ShellSession = [".sh-session", ]
Shen = [".shen", ]
Slash = [".sl", ]
Slim = [".slim", ]
Smali = [".smali", ]
Smalltalk = [".st", ]
Smarty = [".tpl", ]
Solidity = [".sol", ]
SourcePawn = [".sp", ".sma", ]
Squirrel = [".nut", ]
Stan = [".stan", ]
"Standard ML" = [".ML", ".fun", ".sig", ".sml", ]
Stata = [".do", ".ado", ".doh", ".ihlp", ".mata", ".matah", ".sthlp", ]
Stylus = [".styl", ]
SuperCollider = [".scd", ]
Swift = [".swift", ]
SystemVerilog = [".sv", ".svh", ".vh", ]
TOML = [".toml", ]
TXL = [".txl", ]
Tcl = [".tcl", ".adp", ".tm", ]
Tcsh = [".tcsh", ".csh", ]
TeX = [".tex", ".aux", ".bbx", ".bib", ".cbx", ".dtx", ".ins", ".lbx", ".ltx", ".mkii", ".mkiv", ".mkvi", ".sty", ".toc", ]
Tea = [".tea", ]
Text = [".txt", ".no", ]
Textile = [".textile", ]
Thrift = [".thrift", ]
Turing = [".tu", ]
Turtle = [".ttl", ]
Twig = [".twig", ]
TypeScript = [".ts", ".tsx", ]
"Unified Parallel C" = [".upc", ]
"Unity3D Asset" = [".anim", ".asset", ".mat", ".meta", ".prefab", ".unity", ]
Uno = [".uno", ]
UnrealScript = [".uc", ]
UrWeb = [".ur", ".urs", ]
VCL = [".vcl", ]
VHDL = [".vhdl", ".vhd", ".vhf", ".vhi", ".vho", ".vhs", ".vht", ".vhw", ]
Vala = [".vala", ".vapi", ]
Verilog = [".veo", ]
VimL = [".vim", ]
"Visual Basic" = [".vb", ".bas", ".frm", ".frx", ".vba", ".vbhtml", ".vbs", ]
Volt = [".volt", ]
Vue = [".vue", ]
"Web Ontology Language" = [".owl", ]
WebAssembly = [".wat", ]
WebIDL = [".webidl", ]
X10 = [".x10", ]
XC = [".xc", ]
XML = [".xml", ".ant", ".axml", ".ccxml", ".clixml", ".cproject", ".csl", ".csproj", ".ct", ".dita", ".ditamap", ".ditaval", ".dll.config", ".dotsettings", ".filters", ".fsproj", ".fxml", ".glade", ".grxml", ".iml", ".ivy", ".jelly", ".jsproj", ".kml", ".launch", ".mdpolicy", ".mxml", ".nproj", ".nuspec", ".odd", ".osm", ".plist", ".props", ".ps1xml", ".psc1", ".pt", ".rdf", ".rss", ".scxml", ".srdf", ".storyboard", ".stTheme", ".sublime-snippet", ".targets", ".tmCommand", ".tml", ".tmLanguage", ".tmPreferences", ".tmSnippet", ".tmTheme", ".ui", ".urdf", ".ux", ".vbproj", ".vcxproj", ".vssettings", ".vxml", ".wsdl", ".wsf", ".wxi", ".wxl", ".wxs", ".x3d", ".xacro", ".xaml", ".xib", ".xlf", ".xliff", ".xmi", ".xml.dist", ".xproj", ".xsd", ".xul", ".zcml", ]
XPages = [".xsp-config", ".xsp.metadata", ]
XProc = [".xpl", ".xproc", ]
XQuery = [".xquery", ".xq", ".xql", ".xqm", ".xqy", ]
XS = [".xs", ]
XSLT = [".xslt", ".xsl", ]
Xojo = [".xojo_code", ".xojo_menu", ".xojo_report", ".xojo_script", ".xojo_toolbar", ".xojo_window", ]
Xtend = [".xtend", ]
YAML = [".yml", ".reek", ".rviz", ".sublime-syntax", ".syntax", ".yaml", ".yaml-tmlanguage", ]
YANG = [".yang", ]
Yacc = [".y", ".yacc", ".yy", ]
Zephir = [".zep", ]
Zig = [".zig", ]
Zimpl = [".zimpl", ".zmpl", ".zpl", ]
desktop = [".desktop", ".desktop.in", ]
eC = [".ec", ".eh", ]
edn = [".edn", ]
fish = [".fish", ]
mupad = [".mu", ]
nesC = [".nc", ]
ooc = [".ooc", ]
reStructuredText = [".rst", ".rest", ".rest.txt", ".rst.txt", ]
wisp = [".wisp", ]
xBase = [".prg", ".prw", ]
[docs_blacklist_extensions]
# Disable docs for these extensions of text files and scripts that are not programming languages of function, classes and methods
docs_blacklist = ['sql', 'txt', 'yaml', 'json', 'xml', 'md', 'rst', 'rest', 'rest.txt', 'rst.txt', 'mdpolicy', 'mdown', 'markdown', 'mdwn', 'mkd', 'mkdn', 'mkdown', 'sh']

View File

@ -0,0 +1,126 @@
[pr_add_docs_prompt]
system="""You are PR-Doc, a language model that specializes in generating documentation for code components in a Pull Request (PR).
Your task is to generate {{ docs_for_language }} for code components in the PR Diff.
Example for the PR Diff format:
======
## file: 'src/file1.py'
@@ -12,3 +12,4 @@ def func1():
__new hunk__
12 code line1 that remained unchanged in the PR
14 +new code line1 added in the PR
15 +new code line2 added in the PR
16 code line2 that remained unchanged in the PR
__old hunk__
code line1 that remained unchanged in the PR
-code line that was removed in the PR
code line2 that remained unchanged in the PR
@@ ... @@ def func2():
__new hunk__
...
__old hunk__
...
## file: '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 standard {{ 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).
For example, in Python the docs are placed after the function signature, but in Java they are placed before.
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 }}'
{%- if description %}
Description:
======
{{ description|trim }}
======
{%- endif %}
{%- if language %}
Main PR language: '{{language}}'
{%- endif %}
The PR Diff:
======
{{ diff|trim }}
======
Response (should be a valid YAML, and nothing else):
```yaml
"""

View File

@ -0,0 +1,121 @@
[pr_code_suggestions_prompt]
system="""You are PR-Reviewer, a language model that specializes in suggesting code improvements for a Pull Request (PR).
Your task is to provide meaningful and actionable code suggestions, to improve the new code presented in a PR diff (lines starting with '+').
Example for the PR Diff format:
======
## file: 'src/file1.py'
@@ ... @@ def func1():
__new hunk__
12 code line1 that remained unchanged in the PR
13 +new hunk code line2 added in the PR
14 code line3 that remained unchanged in the PR
__old hunk__
code line1 that remained unchanged in the PR
-old hunk code line2 that was removed in the PR
code line3 that remained unchanged in the PR
@@ ... @@ def func2():
__new hunk__
...
__old hunk__
...
## file: 'src/file2.py'
...
======
Specific instructions:
- Provide up to {{ num_code_suggestions }} code suggestions. The suggestions should be diverse and insightful.
- The suggestions should refer only to code from the '__new hunk__' sections, and focus on new lines of code (lines starting with '+').
- Prioritize suggestions that address major problems, issues and bugs in the PR code. As a second priority, suggestions should focus on enhancement, best practice, performance, maintainability, and other aspects.
- Don't suggest to add docstring, type hints, or comments, or to remove unused imports.
- Suggestions should not repeat code already present in the '__new hunk__' sections.
- Provide the exact line numbers range (inclusive) for each suggestion.
- When quoting variables or names from the code, use backticks (`) instead of single quote (').
{%- if extra_instructions %}
Extra instructions from the user:
======
{{ extra_instructions }}
======
{%- endif %}
The output must be a YAML object equivalent to type $PRCodeSuggestions, according to the following Pydantic definitions:
=====
class CodeSuggestion(BaseModel):
relevant_file: str = Field(description="the relevant file full path")
language: str = Field(description="the code language of the relevant file")
suggestion_content: str = Field(description="an actionable suggestion for meaningfully improving the new code introduced in the PR")
{%- if summarize_mode %}
existing_code: str = Field(description="a short code snippet from a '__new hunk__' section to illustrate the relevant existing code. Don't show the line numbers.")
improved_code: str = Field(description="a short code snippet to illustrate the improved code, after applying the suggestion.")
one_sentence_summary:str = Field(description="a short summary of the suggestion action, in a single sentence. Focus on the 'what'. Be general, and avoid method or variable names.")
{%- else %}
existing_code: str = Field(description="a code snippet, demonstrating the relevant code lines from a '__new hunk__' section. It must be contiguous, correctly formatted and indented, and without line numbers")
improved_code: str = Field(description="a new code snippet, that can be used to replace the relevant lines in '__new hunk__' code. Replacement suggestions should be complete, correctly formatted and indented, and without line numbers")
{%- endif %}
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 hunk line numbers, and correspond to the 'existing code' snippet above")
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 hunk line numbers, and correspond to the 'existing code' snippet above")
label: str = Field(description="a single label for the suggestion, to help the user understand the suggestion type. For example: 'security', 'bug', 'performance', 'enhancement', 'possible issue', 'best practice', 'maintainability', etc. Other labels are also allowed")
class PRCodeSuggestions(BaseModel):
code_suggestions: List[CodeSuggestion]
=====
Example output:
```yaml
code_suggestions:
- relevant_file: |
src/file1.py
language: |
python
suggestion_content: |
...
{%- if summarize_mode %}
existing_code: |
...
improved_code: |
...
one_sentence_summary: |
...
relevant_lines_start: 12
relevant_lines_end: 13
{%- else %}
existing_code: |
...
relevant_lines_start: 12
relevant_lines_end: 13
improved_code: |
...
{%- endif %}
label: |
...
```
Each YAML output MUST be after a newline, indented, with block scalar indicator ('|').
"""
user="""PR Info:
Title: '{{title}}'
The PR Diff:
======
{{ diff|trim }}
======
Response (should be a valid YAML, and nothing else):
```yaml
"""

View File

@ -0,0 +1,86 @@
[pr_custom_labels_prompt]
system="""You are PR-Reviewer, a language model designed to review a Git Pull Request (PR).
Your task is to provide labels that describe the PR content.
{%- if enable_custom_labels %}
Thoroughly read the labels name and the provided description, and decide whether the label is relevant to the PR.
{%- endif %}
{%- if extra_instructions %}
Extra instructions from the user:
======
{{ extra_instructions }}
======
{% endif %}
The output must be a YAML object equivalent to type $Labels, according to the following Pydantic definitions:
======
{%- if enable_custom_labels %}
{{ custom_labels_class }}
{%- else %}
class Label(str, Enum):
bug_fix = "Bug fix"
tests = "Tests"
enhancement = "Enhancement"
documentation = "Documentation"
other = "Other"
{%- endif %}
class Labels(BaseModel):
labels: List[Label] = Field(min_items=0, description="choose the relevant custom labels that describe the PR content, and return their keys. Use the value field of the Label object to better understand the label meaning.")
======
Example output:
```yaml
labels:
- ...
- ...
```
Answer should be a valid YAML, and nothing else.
"""
user="""PR Info:
Previous title: '{{title}}'
Branch: '{{ branch }}'
{%- if description %}
Description:
======
{{ description|trim }}
======
{%- endif %}
{%- if language %}
Main PR language: '{{ language }}'
{%- endif %}
{%- if commit_messages_str %}
Commit messages:
======
{{ commit_messages_str|trim }}
======
{%- endif %}
The PR Git Diff:
======
{{ diff|trim }}
======
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.
Response (should be a valid YAML, and nothing else):
```yaml
"""

View File

@ -0,0 +1,130 @@
[pr_description_prompt]
system="""You are PR-Reviewer, a language model designed to review a Git Pull Request (PR).
{%- if enable_custom_labels %}
Your task is to provide a full description for the PR content - files walkthrough, title, type, description and labels.
{%- else %}
Your task is to provide a full description for the PR content - files walkthrough, title, type, and description.
{%- endif %}
- Focus on the new PR code (lines starting with '+').
- Keep in mind that the 'Previous title', 'Previous description' and 'Commit messages' sections may be partial, simplistic, non-informative or out of date. Hence, compare them to the PR diff code, and use them only as a reference.
- The generated title and description should prioritize the most significant changes.
- If needed, each YAML output should be in block scalar indicator ('|-')
- When quoting variables or names from the code, use backticks (`) instead of single quote (').
{%- if extra_instructions %}
Extra instructions from the user:
=====
{{extra_instructions}}
=====
{% endif %}
The output must be a YAML object equivalent to type $PRDescription, according to the following Pydantic definitions:
=====
class PRType(str, Enum):
bug_fix = "Bug fix"
tests = "Tests"
enhancement = "Enhancement"
documentation = "Documentation"
other = "Other"
{%- if enable_custom_labels %}
{{ custom_labels_class }}
{%- endif %}
{%- if enable_semantic_files_types %}
Class FileDescription(BaseModel):
filename: str = Field(description="the relevant file full path")
language: str = Field(description="the relevant file language")
changes_summary: str = Field(description="concise summary of the changes in the relevant file, in bullet points (1-4 bullet points).")
changes_title: str = Field(description="an informative title for the changes in the files, describing its main theme (5-10 words).")
label: str = Field(description="a single semantic label that represents a type of code changes that occurred in the File. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'dependencies', 'formatting', 'miscellaneous', ...")
{%- endif %}
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')")
{%- if enable_semantic_files_types %}
pr_files[List[FileDescription]] = Field(max_items=15, description="a list of the files in the PR, and their changes summary.")
{%- endif %}
description: str = Field(description="an informative and concise description of the PR. Use bullet points. Display first the most significant changes.")
title: str = Field(description="an informative title for the PR, describing its main theme")
{%- if enable_custom_labels %}
labels: List[Label] = Field(min_items=0, description="choose the relevant custom labels that describe the PR content, and return their keys. Use the value field of the Label object to better understand the label meaning.")
{%- endif %}
=====
Example output:
```yaml
type:
- ...
- ...
{%- if enable_semantic_files_types %}
pr_files:
- filename: |
...
language: |
...
changes_summary: |
...
changes_title: |
...
label: |
...
...
{%- endif %}
description: |-
...
title: |-
...
{%- if enable_custom_labels %}
labels:
- |
...
- |
...
{%- 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 ('|')
"""
user="""PR Info:
Previous title: '{{title}}'
{%- if description %}
Previous description:
=====
{{ description|trim }}
=====
{%- endif %}
Branch: '{{branch}}'
{%- if commit_messages_str %}
Commit messages:
=====
{{ commit_messages_str|trim }}
=====
{%- endif %}
The PR Diff:
=====
{{ diff|trim }}
=====
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.
Response (should be a valid YAML, and nothing else):
```yaml
"""

Some files were not shown because too many files have changed in this diff Show More