Compare commits

...

188 Commits

Author SHA1 Message Date
685f001298 s 2024-11-06 14:46:57 +02:00
88c2b90860 static analysis 2024-10-02 09:06:30 +03:00
c84d84ace2 static analysis 2024-10-02 08:56:33 +03:00
Tal
af5a50ac6a Merge pull request #1264 from Codium-ai/tr/commitable_alongside_table
dual publishing mode
2024-10-01 08:38:34 +03:00
bccc2844b9 dual publishing mode 2024-10-01 08:32:29 +03:00
Tal
d80103751e Merge pull request #1263 from Codium-ai/tr/commitable_alongside_table
feat: add dual publishing mode for PR code suggestions
2024-10-01 08:25:00 +03:00
8ff8b1d48e default 2024-10-01 08:22:28 +03:00
da0bd84746 dual 2024-10-01 08:20:16 +03:00
b42ded61f8 docs: add guidelines for implementing proposed code suggestions in improve.md 2024-10-01 08:16:51 +03:00
dfa4f22be2 feat: add dual publishing mode for PR code suggestions
- Introduced dual publishing mode to present high-scoring suggestions as both table entries and commitable PR comments.
- Updated documentation to include configuration options for dual publishing mode.
- Enhanced `pr_code_suggestions.py` to handle dual publishing logic and error handling.
- Modified `configuration.toml` to include `duel_publishing_score_threshold` setting.
2024-10-01 08:01:27 +03:00
Tal
7d55fc174b Merge pull request #1262 from Codium-ai/tr/prompt
style: refine field descriptions in KeyIssuesComponentLink model
2024-09-30 13:06:11 +03:00
968fb71577 refactor: update terminology for issue review recommendations in utils.py 2024-09-30 13:03:42 +03:00
454365913f refactor: update terminology for issue review recommendations in utils.py 2024-09-30 13:00:01 +03:00
bbaba2dbda refactor: update terminology for issue review recommendations in utils.py
style: refine field descriptions in KeyIssuesComponentLink model
2024-09-30 08:58:32 +03:00
e4c6792866 Merge remote-tracking branch 'origin/main' 2024-09-30 07:52:54 +03:00
183dd5d2fc diverse 2024-09-30 07:52:43 +03:00
Tal
2e79392a5f Merge pull request #1256 from Codium-ai/mrT23-patch-2
disable chat message for github action
2024-09-30 07:40:04 +03:00
da4148f336 docs: update links and references to Qodo Merge in documentation 2024-09-29 17:38:02 +03:00
070ed21103 update documentation links to qodo-merge-docs.qodo.ai 2024-09-29 17:32:16 +03:00
Tal
640bc1e28a Merge pull request #1261 from Codium-ai/tr/qodo_merge
Qodo Merge rename
2024-09-29 17:26:13 +03:00
6c4aa468a9 formerly 2024-09-29 17:24:37 +03:00
da20efd050 Qodo Merge rename 2024-09-29 17:19:46 +03:00
c25aaad176 Qodo Merge rename 2024-09-29 17:15:49 +03:00
Tal
b4c20d683c Merge pull request #1260 from Codium-ai/mrT23-patch-3
Update CNAME
2024-09-29 12:26:50 +03:00
Tal
7bc20d6f16 Update CNAME 2024-09-29 12:25:48 +03:00
f894e8831b revert unauthorized merge 2024-09-29 08:37:26 +03:00
25b7e1e777 update docs URL 2024-09-29 08:19:26 +03:00
Tal
6ba2fb212d Merge pull request #1259 from Codium-ai/tr/intro_review
Add intro text option for PR reviews in configuration and utils
2024-09-29 07:32:27 +03:00
4a60046f7c update tests 2024-09-29 07:28:02 +03:00
35b1f5e747 key 2024-09-29 07:23:34 +03:00
d77a819d92 Add intro text option for PR reviews in configuration and utils 2024-09-29 07:06:48 +03:00
f3fd439d47 docs 2024-09-27 16:24:07 +03:00
Tal
6188afff48 Merge pull request #1257 from Codium-ai/tr/code_suggestion_message
docs: update PR-Agent documentation with PR Chat search instructions
2024-09-27 16:15:09 +03:00
57cfdcf274 docs: update PR-Agent documentation with PR Chat search instructions 2024-09-27 16:13:54 +03:00
Tal
1333ac47bc Update configuration.toml 2024-09-26 18:24:11 +03:00
Tal
267e01409b Merge pull request #1255 from Codium-ai/tr/code_suggestion_message
Enable intro and chat text for PR code suggestions in configuration
2024-09-26 17:15:10 +03:00
8bdebcb99f Enable intro and chat text for PR code suggestions in configuration 2024-09-26 17:11:00 +03:00
Tal
a13400b9b8 Merge pull request #1254 from Codium-ai/tr/code_suggestion_message
Add intro and chat text options for PR code suggestions in configuration
2024-09-26 16:31:10 +03:00
89f9cf5adc Add intro and chat text options for PR code suggestions in configuration 2024-09-26 09:07:51 +03:00
Tal
6b653dbe48 Merge pull request #1253 from Codium-ai/tr/code_suggestion_message
Add configuration for auto actions in GitHub Action runner
2024-09-26 08:50:56 +03:00
109b965407 Add configuration for auto actions in GitHub Action runner 2024-09-26 08:03:39 +03:00
Tal
511c5a31db Merge pull request #1252 from Codium-ai/tr/prompts_refactor
improve code suggestion prompt
2024-09-25 21:23:00 +03:00
3dd8050004 improve code suggestion prompt 2024-09-25 21:22:41 +03:00
4b7d01972c improve code suggestion prompt 2024-09-25 21:15:14 +03:00
05ec944a8b improve code suggestion prompt 2024-09-25 17:52:54 +03:00
4713ae74b7 improve code suggestion prompt 2024-09-25 17:42:59 +03:00
c828cdde62 improve code suggestion prompt 2024-09-25 17:41:21 +03:00
6f14f9c8e1 improve code suggestion prompt 2024-09-25 16:22:16 +03:00
Tal
9f8cc75bd3 Merge pull request #1250 from chapeupreto/improve-pipeline-gitlab
docs: improve GitLab installation instruction
2024-09-25 07:11:54 +03:00
0668ccbb9e docs: improve GitLab installation instruction
This commit adds a missing `export` instruction that is required mainly
for self-hosted GitLab installations.
2024-09-24 23:34:40 -03:00
Tal
8a287f8ed6 Merge pull request #1248 from Codium-ai/tr/help_fixes
DocHelper
2024-09-22 16:17:30 +03:00
d5625db3c8 DocHelper 2024-09-22 16:16:59 +03:00
d6b779eef8 DocHelper 2024-09-22 16:14:32 +03:00
804cb9ec1d DocHelper 2024-09-22 15:55:18 +03:00
Tal
47d32283ca Merge pull request #1247 from Codium-ai/tr/help_fixes
Tr/help fixes
2024-09-22 10:23:29 +03:00
397963257d DocHelper 2024-09-22 10:21:47 +03:00
a3fd15bb92 Merge remote-tracking branch 'origin/main' into tr/help_fixes 2024-09-22 09:27:10 +03:00
ded7d96649 DocHelper 2024-09-22 09:23:34 +03:00
Tal
bbf06e25ef Merge pull request #1246 from Codium-ai/tr/docs_update
update docs
2024-09-22 09:20:38 +03:00
7e5ddf7e37 Update improve.md with enhanced self-review configuration details 2024-09-22 09:19:39 +03:00
0198c61cf7 update docs 2024-09-22 09:00:56 +03:00
20d8e76a7f logs 2024-09-22 08:31:56 +03:00
be8052251a Update pr_help_prompts.toml and fix snippet indexing in pr_help_message.py 2024-09-22 08:13:23 +03:00
ba08b13446 update PR-Agent usage instructions in pr_description.py 2024-09-21 21:10:51 +03:00
835684b92a Merge remote-tracking branch 'origin/main'
# Conflicts:
#	pr_agent/tools/pr_description.py
2024-09-21 21:10:38 +03:00
90295b6429 update PR-Agent usage instructions in pr_description.py 2024-09-21 21:10:06 +03:00
Tal
08d6bbc94c Merge pull request #1245 from Codium-ai/tr/help_rag
docs pr help
2024-09-21 20:58:06 +03:00
8229d98842 docs pr help 2024-09-21 20:55:05 +03:00
Tal
9e28aca919 Merge pull request #1244 from Codium-ai/tr/help_rag
Refactor S3 file handling and update Dockerfile to include local Chroma DB file
2024-09-21 19:28:04 +03:00
3e780783cc Add docs db 2024-09-21 19:24:23 +03:00
5c7b65810c Refactor S3 file handling and update Dockerfile to include local Chroma DB file 2024-09-21 19:11:46 +03:00
Tal
f2f82e8805 Merge pull request #1243 from Codium-ai/tr/help_rag
get protection
2024-09-21 17:08:23 +03:00
1e51acff22 get protection 2024-09-21 17:07:46 +03:00
Tal
81e847e477 Merge pull request #1242 from Codium-ai/tr/help_rag
prompts
2024-09-21 16:59:23 +03:00
a70fe27d94 prompts 2024-09-21 16:58:37 +03:00
Tal
1a5835a947 Merge pull request #1241 from Codium-ai/tr/help_rag
Add PR help message functionality and update dependencies
2024-09-21 16:28:42 +03:00
4b74506107 Add PR help message functionality and update dependencies
- Implement PRHelpMessage class to provide AI-powered assistance for pull requests.
- Add methods for similarity search using local, S3, and Pinecone databases.
- Update `requirements.txt` to include new dependencies for langchain and chromadb.
- Modify `configuration.toml` to include `force_local_db` setting for PR help.
- Update `aiohttp` and `openai` package versions.
2024-09-21 16:22:51 +03:00
08319f8492 fixed azure remove comment bug 2024-09-19 12:54:26 +03:00
Tal
b447080777 Merge pull request #1236 from Codium-ai/tr/tldr
TLDR
2024-09-16 09:23:49 +03:00
da398ce56f TLDR 2024-09-16 09:21:52 +03:00
Tal
16763d81b4 Merge pull request #1232 from yzongyue/feature/compatible_with_old_gitlab
func get_line_link compatible with old gitlab versions
2024-09-15 17:06:43 +03:00
Tal
80fe297bc9 Merge pull request #1231 from matanbaruch/main
Update PR Action Handling Logic in GitHub Action Runner
2024-09-15 15:30:34 +03:00
5d68b0c492 Update automations_and_usage.md 2024-09-15 15:19:40 +03:00
Tal
8d5f015e5c Merge pull request #1234 from Codium-ai/tr/dynamic
self_reflection
2024-09-15 14:52:06 +03:00
be03f83318 self_reflection 2024-09-15 14:50:24 +03:00
cbfd250c0c self_reflection 2024-09-15 14:47:27 +03:00
Tal
7ce46e65a1 Merge pull request #1233 from Codium-ai/tr/dynamic
collapsible
2024-09-15 13:45:39 +03:00
600f230ba7 collapsible 2024-09-15 13:44:27 +03:00
Tal
4f4f13b8b2 Update improve.md 2024-09-15 12:44:11 +03:00
Tal
146b8823a9 Merge pull request #1230 from Codium-ai/tr/dynamic
Tr/dynamic
2024-09-15 12:39:00 +03:00
fdb1ff8057 update best practics 2024-09-15 12:36:00 +03:00
ce8e637800 get_line_link compatible with old gitlab versions 2024-09-15 14:56:08 +08:00
306af02d22 Update github_action_runner.py 2024-09-15 09:42:02 +03:00
a23541912b Update github_action_runner.py 2024-09-15 09:40:05 +03:00
0851767774 Update configuration.toml 2024-09-15 09:35:58 +03:00
585a7f1c69 Update github_action_runner.py 2024-09-15 09:33:47 +03:00
8d82cb2e04 f string 2024-09-15 08:50:24 +03:00
7586514abf docs: clarify guidelines on file length and specificity in improve.md 2024-09-15 08:28:01 +03:00
480a025877 Merge remote-tracking branch 'origin/main' into tr/dynamic 2024-09-15 08:08:11 +03:00
8f943a0d44 fix: update error logging messages and system prompt handling in litellm_ai_handler.py 2024-09-15 08:07:59 +03:00
Tal
2102c51422 Merge pull request #1228 from eddielu/eddie/azurecommentstatus
Set azure devops default status to ByDesign
2024-09-14 20:34:13 +03:00
29028d43cf Set azure devops default status to ByDesign 2024-09-13 18:01:08 -07:00
Tal
95d1b0d0c5 Merge pull request #1227 from Codium-ai/tr/dynamic
refactor logic
2024-09-13 22:22:47 +03:00
cc0e432247 refactor logic 2024-09-13 22:17:24 +03:00
0fb158fd47 fix push gitlab 2024-09-13 20:59:45 +03:00
Tal
867a430a38 Merge pull request #1226 from KennyDizi/main
Add new o1-mini and o1-preview models
2024-09-13 17:17:26 +03:00
a94496285f Add o1-preview models 2024-09-13 08:23:18 +07:00
567c144176 Add o1-mini models 2024-09-13 08:22:09 +07:00
Tal
c08b59a74d Merge pull request #1224 from Codium-ai/tr/dynamic_context_
Tr/dynamic context
2024-09-12 11:43:15 +03:00
0ba81e1ac7 docs: add dynamic context strategy documentation and update configuration settings 2024-09-12 11:42:27 +03:00
2cb0dd2496 docs: add dynamic context strategy documentation and update configuration settings 2024-09-12 11:38:54 +03:00
a8367d1a22 docs: add dynamic context strategy documentation and update configuration settings
- Added detailed documentation on the dynamic context strategy in `dynamic_context.md`.
- Updated configuration settings in `configuration.toml` to enable dynamic context by default.
- Adjusted context line parameters in `additional_configurations.md` to reflect new defaults.
- Announced dynamic context as the default option in the `README.md` news section.
2024-09-12 11:37:52 +03:00
Tal
1a3345c6e6 Merge pull request #1223 from Codium-ai/mrT23-patch-12
Update additional_configurations.md
2024-09-12 09:29:02 +03:00
Tal
564845adff Update additional_configurations.md 2024-09-12 09:27:45 +03:00
Tal
3ea691e70a Merge pull request #1216 from Codium-ai/tr/azure_parsing
fix: improve Azure DevOps PR URL parsing and add unit tests
2024-09-12 09:09:24 +03:00
Tal
5047d076f8 Merge pull request #1222 from Codium-ai/tr/docs_and_fixes
enhance: cap patch extra lines and update documentation with separato…
2024-09-12 09:07:06 +03:00
7de6bb0150 enhance: cap patch extra lines and update documentation with separators and context adjustments 2024-09-12 09:05:26 +03:00
a1582b5338 enhance: cap patch extra lines and update documentation with separators and context adjustments 2024-09-12 09:01:36 +03:00
Tal
dd8d78e7d8 Merge pull request #1221 from Codium-ai/tr/updates_and_fixes
FAQ
2024-09-11 16:53:41 +03:00
5af6cc7538 FAQ 2024-09-11 16:52:21 +03:00
Tal
6cc562d6a2 Merge pull request #1220 from Codium-ai/tr/updates_and_fixes
FAQ
2024-09-11 16:41:07 +03:00
19b051b992 FAQ 2024-09-11 16:40:34 +03:00
be68ee89f3 FAQ 2024-09-11 16:36:27 +03:00
db6c75a130 FAQ 2024-09-11 16:28:09 +03:00
Tal
74688846e0 Merge pull request #1219 from Codium-ai/tr/updates_and_fixes
docs and fixes
2024-09-10 20:10:53 +03:00
09b0a04a47 docs and fixes 2024-09-10 20:06:48 +03:00
Tal
cc1b65f886 Merge pull request #1218 from Codium-ai/tr/updates_and_fixes
feat: enhance error handling and logging, update AI metadata terminology
2024-09-10 17:59:48 +03:00
Tal
1451d82d6b Update pr_agent/algo/pr_processing.py
Co-authored-by: codiumai-pr-agent-pro[bot] <151058649+codiumai-pr-agent-pro[bot]@users.noreply.github.com>
2024-09-10 17:50:32 +03:00
01ba6fe63d feat: enhance error handling and logging, update AI metadata terminology
- Improved error handling and logging in `pr_processing.py` and `github_polling.py` to provide more detailed error information.
- Updated AI metadata terminology from "AI-generated file summary" to "AI-generated changes summary" across multiple files for consistency.
- Added a placeholder method `publish_file_comments` in `azuredevops_provider.py`.
- Refined logging messages in `azuredevops_provider.py` for better clarity.
2024-09-10 17:44:26 +03:00
Tal
74f9da1135 Merge pull request #1217 from Codium-ai/mrT23-patch-12
Update index.md
2024-09-10 09:06:55 +03:00
Tal
f80c2ae2c8 Update index.md 2024-09-10 09:05:50 +03:00
e444da8378 fix: improve Azure DevOps PR URL parsing and add unit tests 2024-09-10 08:19:22 +03:00
Tal
25ad8a09ce Merge pull request #1215 from Codium-ai/tr/docs
docs
2024-09-10 08:00:36 +03:00
897e791b1a docs 2024-09-10 08:00:10 +03:00
7f94dda54e docs 2024-09-10 07:58:18 +03:00
Tal
538a592882 Merge pull request #1214 from Codium-ai/tr/docs
docs
2024-09-10 07:53:35 +03:00
a3cb7277a7 docs 2024-09-10 07:51:47 +03:00
Tal
b5cd560402 Merge pull request #1212 from Codium-ai/tr/configurations
docs
2024-09-09 19:47:47 +03:00
fd38c33fcb docs 2024-09-09 19:45:29 +03:00
Tal
f767a3dfde Merge pull request #1211 from Codium-ai/hl/impact_docs
impact docs
2024-09-09 19:34:42 +03:00
9f8b619858 small fix 2024-09-09 18:47:53 +03:00
8de16939ba impact docs 2024-09-09 18:15:56 +03:00
Tal
6ed5537065 Merge pull request #1210 from Codium-ai/tr/configurations
bug fix: remove unused get_pr_description method from Azure DevOps
2024-09-09 16:26:46 +03:00
1a9638cf87 bug fix: remove unused get_pr_description method from Azure DevOps provider 2024-09-09 16:25:34 +03:00
Tal
49521aafff Merge pull request #1208 from Codium-ai/tr/configurations
config docs
2024-09-09 08:34:46 +03:00
c8e8ed89d2 feat: integrate Dynaconf for configuration management and enhance config display 2024-09-09 08:31:20 +03:00
ebc5cafb2b protection 2024-09-08 17:46:21 +03:00
Tal
52e8d7bc6a Merge pull request #1207 from Codium-ai/tr/fixes_metadata
Tr/fixes metadata
2024-09-08 17:32:58 +03:00
f7344fd787 metadata.md docs 2024-09-08 17:31:17 +03:00
86103c65e8 pattern_back 2024-09-08 17:24:13 +03:00
a4658b9960 docs 2024-09-08 16:52:20 +03:00
5fd831c448 impact_evaluation 2024-09-08 16:47:04 +03:00
332d3a0c5e markdown update 2024-09-08 16:43:44 +03:00
Tal
edef712b6a Merge pull request #1206 from Codium-ai/tr/ai_metadata
ai_metadat_injection
2024-09-08 16:33:00 +03:00
1831f2cec4 markdown 2024-09-08 16:32:46 +03:00
8706f643ef enable ai_metadata 2024-09-08 16:26:26 +03:00
Tal
35a75095ea Merge pull request #1205 from ScArLeXiA/fix-typo-in-readme
fix: Typo in README (`Chrom` -> `Chrome`)
2024-09-07 19:20:35 +03:00
0aa296d03e fix: Typo in README (Chrom -> Chrome) 2024-09-08 01:14:41 +09:00
Tal
24f7e8622f Merge pull request #1202 from Codium-ai/tr/ignore_titile_adjustments
avoid_full_files
2024-09-07 11:49:21 +03:00
d01cfe443c avoid_full_files 2024-09-07 11:44:24 +03:00
Tal
6150256040 Merge pull request #1201 from Codium-ai/tr/ignore_titile_adjustments
Tr/ignore titile adjustments
2024-09-07 11:27:28 +03:00
147a8e0ef3 refactor: consolidate PR ignore logic into a single function and update documentation 2024-09-07 11:26:13 +03:00
Tal
9199d84796 Merge pull request #1192 from paolomainardi/feature/1190_exclude_branches_tags
feat: gitlab skip source, target and labels
2024-09-07 10:20:31 +03:00
39913ef12a fix: remove specific configurations 2024-09-06 20:23:33 +02:00
d2a744e70c fix: remove line 2024-09-06 18:42:47 +02:00
be93c52380 fix: remove line 2024-09-06 18:42:28 +02:00
7ccefca35e fix: remove comment 2024-09-06 18:41:36 +02:00
14b4723734 feat: move configuration to a common config section, add documentation 2024-09-06 18:32:46 +02:00
c8f1c03061 fix: correct tuple unpacking in GitHub polling task queue loop 2024-09-05 20:31:17 +03:00
Tal
b02fa22948 Merge pull request #1198 from Codium-ai/tr/polling
Tr/polling
2024-09-05 19:58:51 +03:00
85754d2d79 feat: enhance GitHub polling with synchronous comment processing and improved logging 2024-09-05 16:57:10 +03:00
f0d780c7ec feat: enhance GitHub polling with synchronous comment processing and improved logging 2024-09-05 16:55:10 +03:00
19048ee705 feat: enhance GitHub polling with synchronous comment processing and improved logging 2024-09-05 16:53:31 +03:00
b8d2b263b9 feat: enhance GitHub polling with synchronous comment processing and improved logging and bug fixing 2024-09-05 16:52:47 +03:00
Tal
6f17c08f72 Merge pull request #1197 from proDOOMman/onec_enterprice
Add 1C Enterprise language
2024-09-04 15:20:16 +03:00
65c0bc414f docs: add supported browsers section to Chrome extension documentation 2024-09-04 13:36:44 +03:00
015719134f docs: enhance PR chat section with context-aware explanation in features.md 2024-09-04 12:01:23 +03:00
1ed6b7a54a docs: enhance PR chat section with context-aware explanation in features.md 2024-09-04 11:57:24 +03:00
14067a02db docs: update Chrome extension documentation with installation video link and image update 2024-09-04 11:01:04 +03:00
be75bb6a16 Add 1C Enterprise language 2024-09-04 09:04:39 +03:00
883d945687 Merge remote-tracking branch 'origin/main' 2024-09-04 08:52:32 +03:00
8090115f30 docs: update Chrome extension description and bump version to 0.2.4 2024-09-04 08:52:22 +03:00
Tal
6fa226dee7 Merge pull request #1196 from Codium-ai/mrT23-patch-11
Update README.md
2024-09-03 18:50:47 +03:00
Tal
13c1cdbf90 Update README.md 2024-09-03 18:49:04 +03:00
2f7f60a469 fix: review standardize regex checking 2024-09-02 16:31:19 +02:00
adce35765b feat: implement skip branches for github, add ignore title to gitlab 2024-09-02 16:26:50 +02:00
23af1afa03 feat: gitlab skip source, target and labels 2024-08-30 17:01:18 +02:00
fdcbdfce98 feat: gitlab skip source, target and labels 2024-08-30 16:40:23 +02:00
103 changed files with 4191 additions and 889 deletions

View File

@ -25,11 +25,11 @@ CodiumAI PR-Agent aims to help efficiently review and handle pull requests, by p
</div>
### [Documentation](https://pr-agent-docs.codium.ai/)
- See the [Installation Guide](https://pr-agent-docs.codium.ai/installation/) for instructions on installing PR-Agent on different platforms.
- See the [Installation Guide](https://qodo-merge-docs.qodo.ai/installation/) for instructions on installing PR-Agent on different platforms.
- See the [Usage Guide](https://pr-agent-docs.codium.ai/usage-guide/) for instructions on running PR-Agent tools via different interfaces, such as CLI, PR Comments, or by automatically triggering them when a new PR is opened.
- See the [Usage Guide](https://qodo-merge-docs.qodo.ai/usage-guide/) for instructions on running PR-Agent tools via different interfaces, such as CLI, PR Comments, or by automatically triggering them when a new PR is opened.
- See the [Tools Guide](https://pr-agent-docs.codium.ai/tools/) for a detailed description of the different tools, and the available configurations for each tool.
- See the [Tools Guide](https://qodo-merge-docs.qodo.ai/tools/) for a detailed description of the different tools, and the available configurations for each tool.
## Table of Contents
@ -43,9 +43,23 @@ CodiumAI PR-Agent aims to help efficiently review and handle pull requests, by p
## News and Updates
### September 21, 2024
Need help with PR-Agent? New feature - simply comment `/help "your question"` in a pull request, and PR-Agent will provide you with the [relevant documentation](https://github.com/Codium-ai/pr-agent/pull/1241#issuecomment-2365259334).
<kbd><img src="https://www.codium.ai/images/pr_agent/pr_help_chat.png" width="768"></kbd>
### September 12, 2024
[Dynamic context](https://pr-agent-docs.codium.ai/core-abilities/dynamic_context/) is now the default option for context extension.
This feature enables PR-Agent to dynamically adjusting the relevant context for each code hunk, while avoiding overflowing the model with too much information.
### September 3, 2024
New version of PR-Agent, v0.24 was released. See the [release notes](https://github.com/Codium-ai/pr-agent/releases/tag/v0.24) for more information.
### August 26, 2024
New version of [PR Agent Chrom Extension](https://chromewebstore.google.com/detail/pr-agent-chrome-extension/ephlnjeghhogofkifjloamocljapahnl) was released, with full support of context-aware **PR Chat**. This novel feature is free to use for any open-source repository. See more details in [here](https://pr-agent-docs.codium.ai/chrome-extension/#pr-chat).
New version of [PR Agent Chrome Extension](https://chromewebstore.google.com/detail/pr-agent-chrome-extension/ephlnjeghhogofkifjloamocljapahnl) was released, with full support of context-aware **PR Chat**. This novel feature is free to use for any open-source repository. See more details in [here](https://pr-agent-docs.codium.ai/chrome-extension/#pr-chat).
<kbd><img src="https://www.codium.ai/images/pr_agent/pr_chat_1.png" width="768"></kbd>
@ -248,7 +262,7 @@ Note that when you set your own PR-Agent or use CodiumAI hosted PR-Agent, there
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.
See [here](https://pr-agent-docs.codium.ai/#pr-agent-pro) for a list of features available in PR-Agent Pro.
See [here](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) for a list of features available in PR-Agent Pro.

View File

@ -1,6 +1,7 @@
FROM python:3.12.3 AS base
WORKDIR /app
ADD docs/chroma_db.zip /app/docs/chroma_db.zip
ADD pyproject.toml .
ADD requirements.txt .
RUN pip install . && rm pyproject.toml requirements.txt

View File

@ -1 +1 @@
# [Visit Our Docs Portal](https://pr-agent-docs.codium.ai/)
# [Visit Our Docs Portal](https://qodo-merge-docs.qodo.ai/)

BIN
docs/chroma_db.zip Normal file

Binary file not shown.

View File

@ -1 +1 @@
pr-agent-docs.codium.ai
qodo-merge-docs.qodo.ai

View File

@ -1,5 +1,5 @@
We take your code's security and privacy seriously:
- The Chrome extension will not send your code to any external servers.
- For private repositories, we will first validate the user's identity and permissions. After authentication, we generate responses using the existing PR-Agent Pro integration.
- For private repositories, we will first validate the user's identity and permissions. After authentication, we generate responses using the existing Qodo Merge Pro integration.

View File

@ -1,21 +1,25 @@
### PR Chat
### PR chat
The PR-Chat feature allows to freely chat with your PR code, within your GitHub environment.
It will seamlessly add the PR code as context to your chat session, and provide AI-powered feedback.
It will seamlessly use the PR as context to your chat session, and provide AI-powered feedback.
To enable private chat, simply install the PR-Agent Chrome extension. After installation, each PR's file-changed tab will include a chat box, where you may ask questions about your code.
To enable private chat, simply install the Qodo Merge Chrome extension. After installation, each PR's file-changed tab will include a chat box, where you may ask questions about your code.
This chat session is **private**, and won't be visible to other users.
All open-source repositories are supported.
For private repositories, you will also need to install PR-Agent Pro, After installation, make sure to open at least one new PR to fully register your organization. Once done, you can chat with both new and existing PRs across all installed repositories.
For private repositories, you will also need to install Qodo Merge Pro, After installation, make sure to open at least one new PR to fully register your organization. Once done, you can chat with both new and existing PRs across all installed repositories.
<img src="https://codium.ai/images/pr_agent/pr_chat1.png" width="768">
<img src="https://codium.ai/images/pr_agent/pr_chat2.png" width="768">
#### Context-aware PR chat
Qodo Merge constructs a comprehensive context for each pull request, incorporating the PR description, commit messages, and code changes with extended dynamic context. This contextual information, along with additional PR-related data, forms the foundation for an AI-powered chat session. The agent then leverages this rich context to provide intelligent, tailored responses to user inquiries about the pull request.
<img src="https://codium.ai/images/pr_agent/pr_chat_1.png" width="768">
<img src="https://codium.ai/images/pr_agent/pr_chat_2.png" width="768">
### Toolbar extension
With PR-Agent Chrome extension, it's [easier than ever](https://www.youtube.com/watch?v=gT5tli7X4H4) to interactively configure and experiment with the different tools and configuration options.
With Qodo Merge Chrome extension, it's [easier than ever](https://www.youtube.com/watch?v=gT5tli7X4H4) to interactively configure and experiment with the different tools and configuration options.
For private repositories, after you found the setup that works for you, you can also easily export it as a persistent configuration file, and use it for automatic commands.
@ -23,11 +27,11 @@ For private repositories, after you found the setup that works for you, you can
<img src="https://codium.ai/images/pr_agent/toolbar2.png" width="512">
### PR-Agent filters
### Qodo Merge filters
PR-Agent filters is a sidepanel option. that allows you to filter different message in the conversation tab.
Qodo Merge filters is a sidepanel option. that allows you to filter different message in the conversation tab.
For example, you can choose to present only message from PR-Agent, or filter those messages, focusing only on user's comments.
For example, you can choose to present only message from Qodo Merge, or filter those messages, focusing only on user's comments.
<img src="https://codium.ai/images/pr_agent/pr_agent_filters1.png" width="256">
@ -36,7 +40,7 @@ For example, you can choose to present only message from PR-Agent, or filter tho
### Enhanced code suggestions
PR-Agent Chrome extension adds the following capabilities to code suggestions tool's comments:
Qodo Merge Chrome extension adds the following capabilities to code suggestions tool's comments:
- Auto-expand the table when you are viewing a code block, to avoid clipping.
- Adding a "quote-and-reply" button, that enables to address and comment on a specific suggestion (for example, asking the author to fix the issue)

View File

@ -1,8 +1,14 @@
[PR-Agent Chrome extension](https://chromewebstore.google.com/detail/pr-agent-chrome-extension/ephlnjeghhogofkifjloamocljapahnl) is a collection of tools that integrates seamlessly with your GitHub environment, aiming to enhance your Git usage experience, and providing AI-powered capabilities to your PRs.
[Qodo Merge Chrome extension](https://chromewebstore.google.com/detail/pr-agent-chrome-extension/ephlnjeghhogofkifjloamocljapahnl) is a collection of tools that integrates seamlessly with your GitHub environment, aiming to enhance your Git usage experience, and providing AI-powered capabilities to your PRs.
With a single-click installation you will gain access to a context-aware PR chat with top models, a toolbar extension with multiple AI feedbacks, PR-Agent filters, and additional abilities.
With a single-click installation you will gain access to a context-aware chat on your pull requests code, a toolbar extension with multiple AI feedbacks, Qodo Merge filters, and additional abilities.
All the extension's features are free to use on public repositories. For private repositories, you will need to install in addition to the extension [PR-Agent Pro](https://github.com/apps/codiumai-pr-agent-pro) (fast and easy installation with two weeks of trial, no credit card required).
The extension is powered by top code models like Claude 3.5 Sonnet and GPT4. All the extension's features are free to use on public repositories.
<img src="https://codium.ai/images/pr_agent/pr_chat1.png" width="768">
<img src="https://codium.ai/images/pr_agent/pr_chat2.png" width="768">
For private repositories, you will need to install [Qodo Merge Pro](https://github.com/apps/codiumai-pr-agent-pro) in addition to the extension (Quick GitHub app setup with a 14-day free trial. No credit card needed).
For a demonstration of how to install Qodo Merge Pro and use it with the Chrome extension, please refer to the tutorial video at the provided [link](https://codium.ai/images/pr_agent/private_repos.mp4).
<img src="https://codium.ai/images/pr_agent/PR-AgentChat.gif" width="768">
### Supported browsers
The extension is supported on all Chromium-based browsers, including Google Chrome, Arc, Opera, Brave, and Microsoft Edge.

View File

@ -0,0 +1,2 @@
## Overview
TBD

View File

@ -0,0 +1,47 @@
## Overview - PR Compression Strategy
There are two scenarios:
1. The PR is small enough to fit in a single prompt (including system and user prompt)
2. The PR is too large to fit in a single prompt (including system and user prompt)
For both scenarios, we first use the following strategy
#### Repo language prioritization strategy
We prioritize the languages of the repo based on the following criteria:
1. Exclude binary files and non code files (e.g. images, pdfs, etc)
2. Given the main languages used in the repo
3. We sort the PR files by the most common languages in the repo (in descending order):
* ```[[file.py, file2.py],[file3.js, file4.jsx],[readme.md]]```
### Small PR
In this case, we can fit the entire PR in a single prompt:
1. Exclude binary files and non code files (e.g. images, pdfs, etc)
2. We Expand the surrounding context of each patch to 3 lines above and below the patch
### Large PR
#### Motivation
Pull Requests can be very long and contain a lot of information with varying degree of relevance to the pr-agent.
We want to be able to pack as much information as possible in a single LMM prompt, while keeping the information relevant to the pr-agent.
#### Compression strategy
We prioritize additions over deletions:
- Combine all deleted files into a single list (`deleted files`)
- File patches are a list of hunks, remove all hunks of type deletion-only from the hunks in the file patch
#### Adaptive and token-aware file patch fitting
We use [tiktoken](https://github.com/openai/tiktoken) to tokenize the patches after the modifications described above, and we use the following strategy to fit the patches into the prompt:
1. Within each language we sort the files by the number of tokens in the file (in descending order):
- ```[[file2.py, file.py],[file4.jsx, file3.js],[readme.md]]```
2. Iterate through the patches in the order described above
3. Add the patches to the prompt until the prompt reaches a certain buffer from the max token length
4. If there are still patches left, add the remaining patches as a list called `other modified files` to the prompt until the prompt reaches the max token length (hard stop), skip the rest of the patches.
5. 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
![Core Abilities](https://codium.ai/images/git_patch_logic.png){width=768}

View File

@ -0,0 +1,72 @@
## TL;DR
Qodo Merge uses an **asymmetric and dynamic context strategy** to improve AI analysis of code changes in pull requests.
It provides more context before changes than after, and dynamically adjusts the context based on code structure (e.g., enclosing functions or classes).
This approach balances providing sufficient context for accurate analysis, while avoiding needle-in-the-haystack information overload that could degrade AI performance or exceed token limits.
## Introduction
Pull request code changes are retrieved in a unified diff format, showing three lines of context before and after each modified section, with additions marked by '+' and deletions by '-'.
```
@@ -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...
@@ -26,2 +26,4 @@ def func2():
...
```
This unified diff format can be challenging for AI models to interpret accurately, as it provides limited context for understanding the full scope of code changes.
The presentation of code using '+', '-', and ' ' symbols to indicate additions, deletions, and unchanged lines respectively also differs from the standard code formatting typically used to train AI models.
## Challenges of expanding the context window
While expanding the context window is technically feasible, it presents a more fundamental trade-off:
Pros:
- Enhanced context allows the model to better comprehend and localize the code changes, results (potentially) in more precise analysis and suggestions. Without enough context, the model may struggle to understand the code changes and provide relevant feedback.
Cons:
- Excessive context may overwhelm the model with extraneous information, creating a "needle in a haystack" scenario where focusing on the relevant details (the code that actually changed) becomes challenging.
LLM quality is known to degrade when the context gets larger.
Pull requests often encompass multiple changes across many files, potentially spanning hundreds of lines of modified code. This complexity presents a genuine risk of overwhelming the model with excessive context.
- Increased context expands the token count, increasing processing time and cost, and may prevent the model from processing the entire pull request in a single pass.
## Asymmetric and dynamic context
To address these challenges, Qodo Merge employs an **asymmetric** and **dynamic** context strategy, providing the model with more focused and relevant context information for each code change.
**Asymmetric:**
We start by recognizing that the context preceding a code change is typically more crucial for understanding the modification than the context following it.
Consequently, Qodo Merge implements an asymmetric context policy, decoupling the context window into two distinct segments: one for the code before the change and another for the code after.
By independently adjusting each context window, Qodo Merge can supply the model with a more tailored and pertinent context for individual code changes.
**Dynamic:**
We also employ a "dynamic" context strategy.
We start by recognizing that the optimal context for a code change often corresponds to its enclosing code component (e.g., function, class), rather than a fixed number of lines.
Consequently, we dynamically adjust the context window based on the code's structure, ensuring the model receives the most pertinent information for each modification.
To prevent overwhelming the model with excessive context, we impose a limit on the number of lines searched when identifying the enclosing component.
This balance allows for comprehensive understanding while maintaining efficiency and limiting context token usage.
## Appendix - relevant configuration options
```
[config]
patch_extension_skip_types =[".md",".txt"] # Skip files with these extensions when trying to extend the context
allow_dynamic_context=true # Allow dynamic context extension
max_extra_lines_before_dynamic_context = 8 # will try to include up to X extra lines before the hunk in the patch, until we reach an enclosing function or class
patch_extra_lines_before = 3 # Number of extra lines (+3 default ones) to include before each hunk in the patch
patch_extra_lines_after = 1 # Number of extra lines (+3 default ones) to include after each hunk in the patch
```

View File

@ -0,0 +1,44 @@
# Overview - Impact Evaluation 💎
Demonstrating the return on investment (ROI) of AI-powered initiatives is crucial for modern organizations.
To address this need, Qodo Merge has developed an AI impact measurement tools and metrics, providing advanced analytics to help businesses quantify the tangible benefits of AI adoption in their PR review process.
## Auto Impact Validator - Real-Time Tracking of Implemented Qodo Merge Suggestions
### How It Works
When a user pushes a new commit to the pull request, Qodo Merge automatically compares the updated code against the previous suggestions, marking them as implemented if the changes address these recommendations, whether directly or indirectly:
1. **Direct Implementation:** The user directly addresses the suggestion as-is in the PR, either by clicking on the "apply code suggestion" checkbox or by making the changes manually.
2. **Indirect Implementation:** Qodo Merge recognizes when a suggestion's intent is fulfilled, even if the exact code changes differ from the original recommendation. It marks these suggestions as implemented, acknowledging that users may achieve the same goal through alternative solutions.
### Real-Time Visual Feedback
Upon confirming that a suggestion was implemented, Qodo Merge automatically adds a ✅ (check mark) to the relevant suggestion, enabling transparent tracking of Qodo Merge's impact analysis.
Qodo Merge will also add, inside the relevant suggestions, an explanation of how the new code was impacted by each suggestion.
![Suggestion_checkmark](https://codium.ai/images/pr_agent/auto_suggestion_checkmark.png){width=512}
### Dashboard Metrics
The dashboard provides macro-level insights into the overall impact of Qodo Merge on the pull-request process with key productivity metrics.
By offering clear, data-driven evidence of Qodo Merge's impact, it empowers leadership teams to make informed decisions about the tool's effectiveness and ROI.
Here are key metrics that the dashboard tracks:
#### Qodo Merge Impacts per 1K Lines
![Dashboard](https://codium.ai/images/pr_agent/impacts_per_1k_llines.png){width=512}
> Explanation: for every 1K lines of code (additions/edits), Qodo Merge had on average ~X suggestions implemented.
**Why This Metric Matters:**
1. **Standardized and Comparable Measurement:** By measuring impacts per 1K lines of code additions, you create a standardized metric that can be compared across different projects, teams, customers, and time periods. This standardization is crucial for meaningful analysis, benchmarking, and identifying where Qodo Merge is most effective.
2. **Accounts for PR Variability and Incentivizes Quality:** This metric addresses the fact that "Not all PRs are created equal." By normalizing against lines of code rather than PR count, you account for the variability in PR sizes and focus on the quality and impact of suggestions rather than just the number of PRs affected.
3. **Quantifies Value and ROI:** The metric directly correlates with the value Qodo Merge is providing, showing how frequently it offers improvements relative to the amount of new code being written. This provides a clear, quantifiable way to demonstrate Qodo Merge's return on investment to stakeholders.
#### Suggestion Effectiveness Across Categories
![Impacted_Suggestion_Score](https://codium.ai/images/pr_agent/impact_by_category.png){width=512}
> Explanation: This chart illustrates the distribution of implemented suggestions across different categories, enabling teams to better understand Qodo Merge's impact on various aspects of code quality and development practices.
#### Suggestion Score Distribution
![Impacted_Suggestion_Score](https://codium.ai/images/pr_agent/impacted_score_dist.png){width=512}
> Explanation: The distribution of the suggestion score for the implemented suggestions, ensuring that higher-scored suggestions truly represent more significant improvements.

View File

@ -1,52 +1,12 @@
## PR Compression Strategy
There are two scenarios:
# Core Abilities
Qodo Merge utilizes a variety of core abilities to provide a comprehensive and efficient code review experience. These abilities include:
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)
For both scenarios, we first use the following strategy
#### Repo language prioritization strategy
We prioritize the languages of the repo based on the following criteria:
1. Exclude binary files and non code files (e.g. images, pdfs, etc)
2. Given the main languages used in the repo
3. We sort the PR files by the most common languages in the repo (in descending order):
* ```[[file.py, file2.py],[file3.js, file4.jsx],[readme.md]]```
### Small PR
In this case, we can fit the entire PR in a single prompt:
1. Exclude binary files and non code files (e.g. images, pdfs, etc)
2. We Expand the surrounding context of each patch to 3 lines above and below the patch
### Large PR
#### Motivation
Pull Requests can be very long and contain a lot of information with varying degree of relevance to the pr-agent.
We want to be able to pack as much information as possible in a single LMM prompt, while keeping the information relevant to the pr-agent.
#### Compression strategy
We prioritize additions over deletions:
- Combine all deleted files into a single list (`deleted files`)
- File patches are a list of hunks, remove all hunks of type deletion-only from the hunks in the file patch
#### Adaptive and token-aware file patch fitting
We use [tiktoken](https://github.com/openai/tiktoken) to tokenize the patches after the modifications described above, and we use the following strategy to fit the patches into the prompt:
1. Within each language we sort the files by the number of tokens in the file (in descending order):
- ```[[file2.py, file.py],[file4.jsx, file3.js],[readme.md]]```
2. Iterate through the patches in the order described above
3. Add the patches to the prompt until the prompt reaches a certain buffer from the max token length
4. If there are still patches left, add the remaining patches as a list called `other modified files` to the prompt until the prompt reaches the max token length (hard stop), skip the rest of the patches.
5. 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
![Core Abilities](https://codium.ai/images/git_patch_logic.png){width=768}
## YAML Prompting
TBD
## Static Code Analysis 💎
TBD
- [Local and global metadata](https://qodo-merge-docs.qodo.ai/core-abilities/metadata/)
- [Dynamic context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic_context/)
- [Self-reflection](https://qodo-merge-docs.qodo.ai/core-abilities/self_reflection/)
- [Impact evaluation](https://qodo-merge-docs.qodo.ai/core-abilities/impact_evaluation/)
- [Interactivity](https://qodo-merge-docs.qodo.ai/core-abilities/interactivity/)
- [Compression strategy](https://qodo-merge-docs.qodo.ai/core-abilities/compression_strategy/)
- [Code-oriented YAML](https://qodo-merge-docs.qodo.ai/core-abilities/code_oriented_yaml/)
- [Static code analysis](https://qodo-merge-docs.qodo.ai/core-abilities/static_code_analysis/)
- [Code fine-tuning benchmark](https://qodo-merge-docs.qodo.ai/finetuning_benchmark/)

View File

@ -0,0 +1,2 @@
## Interactive invocation 💎
TBD

View File

@ -0,0 +1,56 @@
## Local and global metadata injection with multi-stage analysis
(1)
Qodo Merge initially retrieves for each PR the following data:
- PR title and branch name
- PR original description
- Commit messages history
- PR diff patches, in [hunk diff](https://loicpefferkorn.net/2014/02/diff-files-what-are-hunks-and-how-to-extract-them/) format
- The entire content of the files that were modified in the PR
!!! tip "Tip: Organization-level metadata"
In addition to the inputs above, Qodo Merge can incorporate supplementary preferences provided by the user, like [`extra_instructions` and `organization best practices`](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions-and-best-practices). This information can be used to enhance the PR analysis.
(2)
By default, the first command that Qodo Merge executes is [`describe`](https://qodo-merge-docs.qodo.ai/tools/describe/), which generates three types of outputs:
- PR Type (e.g. bug fix, feature, refactor, etc)
- PR Description - a bullet point summary of the PR
- Changes walkthrough - for each modified file, provide a one-line summary followed by a detailed bullet point list of the changes.
These AI-generated outputs are now considered as part of the PR metadata, and can be used in subsequent commands like `review` and `improve`.
This effectively enables multi-stage chain-of-thought analysis, without doing any additional API calls which will cost time and money.
For example, when generating code suggestions for different files, Qodo Merge can inject the AI-generated ["Changes walkthrough"](https://github.com/Codium-ai/pr-agent/pull/1202#issue-2511546839) file summary in the prompt:
```
## File: 'src/file1.py'
### AI-generated file summary:
- edited function `func1` that does X
- Removed function `func2` that was not used
- ....
@@ ... @@ def func1():
__new hunk__
11 unchanged code line0 in the PR
12 unchanged code line1 in the PR
13 +new code line2 added in the PR
14 unchanged code line3 in the PR
__old hunk__
unchanged code line0
unchanged code line1
-old code line2 removed in the PR
unchanged code line3
@@ ... @@ def func2():
__new hunk__
...
__old hunk__
...
```
(3) The entire PR files that were retrieved are also used to expand and enhance the PR context (see [Dynamic Context](https://qodo-merge-docs.qodo.ai/core-abilities/dynamic-context/)).
(4) All the metadata described above represents several level of cumulative analysis - ranging from hunk level, to file level, to PR level, to organization level.
This comprehensive approach enables Qodo Merge AI models to generate more precise and contextually relevant suggestions and feedback.

View File

@ -0,0 +1,51 @@
## TL;DR
Qodo Merge implements a **self-reflection** process where the AI model reflects, scores, and re-ranks its own suggestions, eliminating irrelevant or incorrect ones.
This approach improves the quality and relevance of suggestions, saving users time and enhancing their experience.
Configuration options allow users to set a score threshold for further filtering out suggestions.
## Introduction - Efficient Review with Hierarchical Presentation
Given that not all generated code suggestions will be relevant, it is crucial to enable users to review them in a fast and efficient way, allowing quick identification and filtering of non-applicable ones.
To achieve this goal, Qodo Merge offers a dedicated hierarchical structure when presenting suggestions to users:
- A "category" section groups suggestions by their category, allowing users to quickly dismiss irrelevant suggestions.
- Each suggestion is first described by a one-line summary, which can be expanded to a full description by clicking on a collapsible.
- Upon expanding a suggestion, the user receives a more comprehensive description, and a code snippet demonstrating the recommendation.
!!! note "Fast Review"
This hierarchical structure is designed to facilitate rapid review of each suggestion, with users spending an average of ~5-10 seconds per item.
## Self-reflection and Re-ranking
The AI model is initially tasked with generating suggestions, and outputting them in order of importance.
However, in practice we observe that models often struggle to simultaneously generate high-quality code suggestions and rank them well in a single pass.
Furthermore, the initial set of generated suggestions sometimes contains easily identifiable errors.
To address these issues, we implemented a "self-reflection" process that refines suggestion ranking and eliminates irrelevant or incorrect proposals.
This process consists of the following steps:
1. Presenting the generated suggestions to the model in a follow-up call.
2. Instructing the model to score each suggestion on a scale of 0-10 and provide a rationale for the assigned score.
3. Utilizing these scores to re-rank the suggestions and filter out incorrect ones (with a score of 0).
4. Optionally, filtering out all suggestions below a user-defined score threshold.
Note that presenting all generated suggestions simultaneously provides the model with a comprehensive context, enabling it to make more informed decisions compared to evaluating each suggestion individually.
To conclude, the self-reflection process enables Qodo Merge to prioritize suggestions based on their importance, eliminate inaccurate or irrelevant proposals, and optionally exclude suggestions that fall below a specified threshold of significance.
This results in a more refined and valuable set of suggestions for the user, saving time and improving the overall experience.
## Example Results
![self_reflection](https://codium.ai/images/pr_agent/self_reflection1.png){width=768}
![self_reflection](https://codium.ai/images/pr_agent/self_reflection2.png){width=768}
## Appendix - Relevant Configuration Options
```
[pr_code_suggestions]
self_reflect_on_suggestions = true # Enable self-reflection on code suggestions
suggestions_score_threshold = 0 # Filter out suggestions with a score below this threshold (0-10)
```

View File

@ -0,0 +1,70 @@
## Overview - Static Code Analysis 💎
By combining static code analysis with LLM capabilities, Qodo Merge can provide a comprehensive analysis of the PR code changes on a component level.
It scans the PR code changes, finds all the code components (methods, functions, classes) that changed, and enables to interactively generate tests, docs, code suggestions and similar code search for each component.
!!! note "Language that are currently supported:"
Python, Java, C++, JavaScript, TypeScript, C#.
## Capabilities
### Analyze PR
The [`analyze`](https://qodo-merge-docs.qodo.ai/tools/analyze/) tool enables to interactively generate tests, docs, code suggestions and similar code search for each component that changed in the PR.
It can be invoked manually by commenting on any PR:
```
/analyze
```
An example result:
![Analyze 1](https://codium.ai/images/pr_agent/analyze_1.png){width=768}
Clicking on each checkbox will trigger the relevant tool for the selected component.
### Generate Tests
The [`test`](https://qodo-merge-docs.qodo.ai/tools/test/) tool generate tests for a selected component, based on the PR code changes.
It can be invoked manually by commenting on any PR:
```
/test component_name
```
where 'component_name' is the name of a specific component in the PR, Or be triggered interactively by using the `analyze` tool.
![test1](https://codium.ai/images/pr_agent/test1.png){width=768}
### Generate Docs for a Component
The [`add_docs`](https://qodo-merge-docs.qodo.ai/tools/documentation/) tool scans the PR code changes, and automatically generate docstrings for any code components that changed in the PR.
It can be invoked manually by commenting on any PR:
```
/add_docs component_name
```
Or be triggered interactively by using the `analyze` tool.
![Docs single component](https://codium.ai/images/pr_agent/docs_single_component.png){width=768}
### Generate Code Suggestions for a Component
The [`improve_component`](https://qodo-merge-docs.qodo.ai/tools/improve_component/) tool generates code suggestions for a specific code component that changed in the PR.
It can be invoked manually by commenting on any PR:
```
/improve_component component_name
```
Or be triggered interactively by using the `analyze` tool.
![improve_component2](https://codium.ai/images/pr_agent/improve_component2.png){width=768}
### Find Similar Code
The [`similar code`](https://qodo-merge-docs.qodo.ai/tools/similar_code/) tool retrieves the most similar code components from inside the organization's codebase, or from open-source code.
For example:
`Global Search` for a method called `chat_completion`:
![similar code global](https://codium.ai/images/pr_agent/similar_code_global2.png){width=768}

67
docs/docs/faq/index.md Normal file
View File

@ -0,0 +1,67 @@
# FAQ
??? note "Question: Can Qodo Merge serve as a substitute for a human reviewer?"
#### Answer:<span style="display:none;">1</span>
Qodo Merge is designed to assist, not replace, human reviewers.
Reviewing PRs is a tedious and time-consuming task often seen as a "chore". In addition, the longer the PR the shorter the relative feedback, since long PRs can overwhelm reviewers, both in terms of technical difficulty, and the actual review time.
Qodo Merge aims to address these pain points, and to assist and empower both the PR author and reviewer.
However, Qodo Merge has built-in safeguards to ensure the developer remains in the driver's seat. For example:
1. Preserves user's original PR header
2. Places user's description above the AI-generated PR description
3. Cannot approve PRs; approval remains reviewer's responsibility
4. The code suggestions are optional, and aim to:
- Encourage self-review and self-reflection
- Highlight potential bugs or oversights
- Enhance code quality and promote best practices
Read more about this issue in our [blog](https://www.codium.ai/blog/understanding-the-challenges-and-pain-points-of-the-pull-request-cycle/)
___
??? note "Question: I received an incorrect or irrelevant suggestion. Why?"
#### Answer:<span style="display:none;">2</span>
- Modern AI models, like Claude 3.5 Sonnet and GPT-4, are improving rapidly but remain imperfect. Users should critically evaluate all suggestions rather than accepting them automatically.
- AI errors are rare, but possible. A main value from reviewing the code suggestions lies in their high probability of catching **mistakes or bugs made by the PR author**. We believe it's worth spending 30-60 seconds reviewing suggestions, even if some aren't relevant, as this practice can enhances code quality and prevent bugs in production.
- The hierarchical structure of the suggestions is designed to help the user to _quickly_ understand them, and to decide which ones are relevant and which are not:
- Only if the `Category` header is relevant, the user should move to the summarized suggestion description.
- Only if the summarized suggestion description is relevant, the user should click on the collapsible, to read the full suggestion description with a code preview example.
- In addition, we recommend to use the [`extra_instructions`](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions-and-best-practices) field to guide the model to suggestions that are more relevant to the specific needs of the project.
- The interactive [PR chat](https://qodo-merge-docs.qodo.ai/chrome-extension/) also provides an easy way to get more tailored suggestions and feedback from the AI model.
___
??? note "Question: How can I get more tailored suggestions?"
#### Answer:<span style="display:none;">3</span>
See [here](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions-and-best-practices) for more information on how to use the `extra_instructions` and `best_practices` configuration options, to guide the model to more tailored suggestions.
___
??? note "Question: Will you store my code ? Are you using my code to train models?"
#### Answer:<span style="display:none;">4</span>
No. Qodo Merge strict privacy policy ensures that your code is not stored or used for training purposes.
For a detailed overview of our data privacy policy, please refer to [this link](https://qodo-merge-docs.qodo.ai/overview/data_privacy/)
___
??? note "Question: Can I use my own LLM keys with Qodo Merge?"
#### Answer:<span style="display:none;">5</span>
When you self-host, you use your own keys.
Qodo Merge Pro with SaaS deployment is a hosted version of Qodo Merge, where Qodo manages the infrastructure and the keys.
For enterprise customers, on-prem deployment is also available. [Contact us](https://www.codium.ai/contact/#pricing) for more information.
___

View File

@ -1,10 +1,10 @@
# PR-Agent Code Fine-tuning Benchmark
# Qodo Merge Code Fine-tuning Benchmark
On coding tasks, the gap between open-source models and top closed-source models such as GPT4 is significant.
<br>
In practice, open-source models are unsuitable for most real-world code tasks, and require further fine-tuning to produce acceptable results.
_PR-Agent fine-tuning benchmark_ aims to benchmark open-source models on their ability to be fine-tuned for a coding task.
_Qodo Merge fine-tuning benchmark_ aims to benchmark open-source models on their ability to be fine-tuned for a coding task.
Specifically, we chose to fine-tune open-source models on the task of analyzing a pull request, and providing useful feedback and code suggestions.
Here are the results:
@ -53,8 +53,8 @@ Here are the results:
### Training dataset
Our training dataset comprises 25,000 pull requests, aggregated from permissive license repos. For each pull request, we generated responses for the three main tools of PR-Agent:
[Describe](https://pr-agent-docs.codium.ai/tools/describe/), [Review](https://pr-agent-docs.codium.ai/tools/improve/) and [Improve](https://pr-agent-docs.codium.ai/tools/improve/).
Our training dataset comprises 25,000 pull requests, aggregated from permissive license repos. For each pull request, we generated responses for the three main tools of Qodo Merge:
[Describe](https://qodo-merge-docs.qodo.ai/tools/describe/), [Review](https://qodo-merge-docs.qodo.ai/tools/improve/) and [Improve](https://qodo-merge-docs.qodo.ai/tools/improve/).
On the raw data collected, we employed various automatic and manual cleaning techniques to ensure the outputs were of the highest quality, and suitable for instruct-tuning.

View File

@ -1,25 +1,38 @@
# Overview
CodiumAI PR-Agent is an open-source tool to help efficiently review and handle pull requests.
Qodo Merge is an open-source tool to help efficiently review and handle pull requests.
- See the [Installation Guide](./installation/index.md) for instructions on installing and running the tool on different git platforms.
- See the [Usage Guide](./usage-guide/index.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 [Usage Guide](./usage-guide/index.md) for instructions on running the Qodo Merge commands via different interfaces, including _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened.
- See the [Tools Guide](./tools/index.md) for a detailed description of the different tools.
## PR-Agent Features
PR-Agent offers extensive pull request functionalities across various git providers.
## Qodo Merge Docs Smart Search
To search the documentation site using natural language:
1) Comment `/help "your question"` in either:
- A pull request where Qodo Merge is installed
- A [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat)
2) Qodo Merge will respond with an [answer](https://github.com/Codium-ai/pr-agent/pull/1241#issuecomment-2365259334) that includes relevant documentation links.
## Qodo Merge Features
Qodo Merge offers extensive pull request functionalities across various git providers.
| | | GitHub | Gitlab | Bitbucket | Azure DevOps |
|-------|-----------------------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|
| TOOLS | Review | ✅ | ✅ | ✅ | ✅ |
| | ⮑ Incremental | ✅ | | | |
| | ⮑ [SOC2 Compliance](https://pr-agent-docs.codium.ai/tools/review/#soc2-ticket-compliance){:target="_blank"} 💎 | ✅ | ✅ | ✅ | |
| | ⮑ [SOC2 Compliance](https://qodo-merge-docs.qodo.ai/tools/review/#soc2-ticket-compliance){:target="_blank"} 💎 | ✅ | ✅ | ✅ | |
| | Ask | ✅ | ✅ | ✅ | ✅ |
| | Describe | ✅ | ✅ | ✅ | ✅ |
| | ⮑ [Inline file summary](https://pr-agent-docs.codium.ai/tools/describe/#inline-file-summary){:target="_blank"} 💎 | ✅ | ✅ | | |
| | ⮑ [Inline file summary](https://qodo-merge-docs.qodo.ai/tools/describe/#inline-file-summary){:target="_blank"} 💎 | ✅ | ✅ | | |
| | Improve | ✅ | ✅ | ✅ | ✅ |
| | ⮑ Extended | ✅ | ✅ | ✅ | ✅ |
| | [Custom Prompt](./tools/custom_prompt.md){:target="_blank"} 💎 | ✅ | ✅ | ✅ | |
@ -42,7 +55,7 @@ PR-Agent offers extensive pull request functionalities across various git provid
| | [Static code analysis](./tools/analyze.md/){:target="_blank"} 💎 | ✅ | ✅ | ✅ | |
| | [Multiple configuration options](./usage-guide/configuration_options.md){:target="_blank"} 💎 | ✅ | ✅ | ✅ | |
💎 marks a feature available only in [PR-Agent Pro](https://www.codium.ai/pricing/){:target="_blank"}
💎 marks a feature available only in [Qodo Merge Pro](https://www.codium.ai/pricing/){:target="_blank"}
## Example Results
@ -74,8 +87,8 @@ PR-Agent offers extensive pull request functionalities across various git provid
## How it Works
The following diagram illustrates PR-Agent tools and their flow:
The following diagram illustrates Qodo Merge tools and their flow:
![PR-Agent Tools](https://codium.ai/images/pr_agent/diagram-v0.9.png)
![Qodo Merge Tools](https://codium.ai/images/pr_agent/diagram-v0.9.png)
Check out the [PR Compression strategy](core-abilities/index.md) page for more details on how we convert a code diff to a manageable LLM prompt
Check out the [core abilities](core-abilities/index.md) page for a comprehensive overview of the variety of core abilities used by Qodo Merge.

View File

@ -1,5 +1,5 @@
## Azure DevOps Pipeline
You can use a pre-built Action Docker image to run PR-Agent as an Azure devops pipeline.
You can use a pre-built Action Docker image to run Qodo Merge as an Azure devops pipeline.
add the following file to your repository under `azure-pipelines.yml`:
```yaml
# Opt out of CI triggers
@ -47,11 +47,11 @@ stages:
env:
azure_devops__pat: $(azure_devops_pat)
openai__key: $(OPENAI_KEY)
displayName: 'Run PR Agent'
displayName: 'Run Qodo Merge'
```
This script will run PR-Agent on every new merge request, with the `improve`, `review`, and `describe` commands.
This script will run Qodo Merge on every new merge request, with the `improve`, `review`, and `describe` commands.
Note that you need to export the `azure_devops__pat` and `OPENAI_KEY` variables in the Azure DevOps pipeline settings (Pipelines -> Library -> + Variable group):
![PR Agent Pro](https://codium.ai/images/pr_agent/azure_devops_pipeline_secrets.png){width=468}
![Qodo Merge Pro](https://codium.ai/images/pr_agent/azure_devops_pipeline_secrets.png){width=468}
Make sure to give pipeline permissions to the `pr_agent` variable group.

View File

@ -1,7 +1,7 @@
## Run as a Bitbucket Pipeline
You can use the Bitbucket Pipeline system to run PR-Agent on every pull request open or update.
You can use the Bitbucket Pipeline system to run Qodo Merge on every pull request open or update.
1. Add the following file in your repository bitbucket_pipelines.yml
@ -29,7 +29,7 @@ Note that comments on a PR are not supported in Bitbucket Pipeline.
## Run using CodiumAI-hosted Bitbucket app 💎
Please contact visit [PR-Agent pro](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.
Please contact visit [Qodo Merge Pro](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
@ -58,7 +58,7 @@ python cli.py --pr_url https://git.onpreminstanceofbitbucket.com/projects/PROJEC
### Run it as service
To run pr-agent as webhook, build the docker image:
To run Qodo Merge 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

View File

@ -1,6 +1,6 @@
## Run as a GitHub Action
You can use our pre-built Github Action Docker image to run PR-Agent as a Github Action.
You can use our pre-built Github Action Docker image to run Qodo Merge as a Github Action.
1) Add the following file to your repository under `.github/workflows/pr_agent.yml`:
@ -60,7 +60,7 @@ The GITHUB_TOKEN secret is automatically created by GitHub.
3) Merge this change to your main branch.
When you open your next PR, you should see a comment from `github-actions` bot with a review of your PR, and instructions on how to use the rest of the tools.
4) You may configure PR-Agent by adding environment variables under the env section corresponding to any configurable property in the [configuration](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) file. Some examples:
4) You may configure Qodo Merge by adding environment variables under the env section corresponding to any configurable property in the [configuration](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) file. Some examples:
```yaml
env:
# ... previous environment values
@ -68,7 +68,7 @@ When you open your next PR, you should see a comment from `github-actions` bot w
PR_REVIEWER.REQUIRE_TESTS_REVIEW: "false" # Disable tests review
PR_CODE_SUGGESTIONS.NUM_CODE_SUGGESTIONS: 6 # Increase number of code suggestions
```
See detailed usage instructions in the [USAGE GUIDE](https://pr-agent-docs.codium.ai/usage-guide/automations_and_usage/#github-action)
See detailed usage instructions in the [USAGE GUIDE](https://qodo-merge-docs.qodo.ai/usage-guide/automations_and_usage/#github-action)
---
@ -155,7 +155,7 @@ cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml
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.
> **Note:** When running Qodo Merge from GitHub app, the default configuration file (configuration.toml) will be loaded.
> However, you can override the default tool parameters by uploading a local configuration file `.pr_agent.toml`
> For more information please check out the [USAGE GUIDE](../usage-guide/automations_and_usage.md#github-app)
---
@ -185,7 +185,7 @@ For example: `GITHUB.WEBHOOK_SECRET` --> `GITHUB__WEBHOOK_SECRET`
## 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:
Not all features have been added to CodeCommit yet. As of right now, CodeCommit has been implemented to run the Qodo Merge CLI on the command line, using AWS credentials stored in environment variables. (More features will be added in the future.) The following is a set of instructions to have Qodo Merge do a review of your CodeCommit pull request from the command line:
1. Create an IAM user that you will use to read CodeCommit pull requests and post comments
* Note: That user should have CLI access only, not Console access

View File

@ -1,5 +1,5 @@
## Run as a GitLab Pipeline
You can use a pre-built Action Docker image to run PR-Agent as a GitLab pipeline. This is a simple way to get started with PR-Agent without setting up your own server.
You can use a pre-built Action Docker image to run Qodo Merge as a GitLab pipeline. This is a simple way to get started with Qodo Merge without setting up your own server.
(1) Add the following file to your repository under `.gitlab-ci.yml`:
```yaml
@ -8,7 +8,7 @@ stages:
pr_agent_job:
stage: pr_agent
image:
image:
name: codiumai/pr-agent:latest
entrypoint: [""]
script:
@ -16,7 +16,8 @@ pr_agent_job:
- echo "Running PR Agent action step"
- export MR_URL="$CI_MERGE_REQUEST_PROJECT_URL/merge_requests/$CI_MERGE_REQUEST_IID"
- echo "MR_URL=$MR_URL"
- export gitlab__PERSONAL_ACCESS_TOKEN=$GITLAB_PERSONAL_ACCESS_TOKEN
- export gitlab__url=$CI_SERVER_PROTOCOL://$CI_SERVER_FQDN
- export gitlab__PERSONAL_ACCESS_TOKEN=$GITLAB_PERSONAL_ACCESS_TOKEN
- export config__git_provider="gitlab"
- export openai__key=$OPENAI_KEY
- python -m pr_agent.cli --pr_url="$MR_URL" describe
@ -25,8 +26,8 @@ pr_agent_job:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
```
This script will run PR-Agent on every new merge request. You can modify the `rules` section to run PR-Agent on different events.
You can also modify the `script` section to run different PR-Agent commands, or with different parameters by exporting different environment variables.
This script will run Qodo Merge on every new merge request. You can modify the `rules` section to run Qodo Merge on different events.
You can also modify the `script` section to run different Qodo Merge commands, or with different parameters by exporting different environment variables.
(2) Add the following masked variables to your GitLab repository (CI/CD -> Variables):
@ -48,13 +49,13 @@ Note that if your base branches are not protected, don't set the variables as `p
```
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 [here](https://pr-agent-docs.codium.ai/installation/github/#run-as-a-github-app) steps 4-7.
3. Follow the instructions to build the Docker image, setup a secrets file and deploy on your own server from [here](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-app) steps 4-7.
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](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml)
5. Create a webhook in GitLab. Set the URL to ```http[s]://<PR_AGENT_HOSTNAME>/webhook```. Set the secret token to the generated secret from step 2.
In the "Trigger" section, check the comments and merge request events boxes.

View File

@ -1,12 +1,12 @@
# Installation
## Self-hosted PR-Agent
If you choose to host you own PR-Agent, you first need to acquire two tokens:
## Self-hosted Qodo Merge
If you choose to host you own Qodo Merge, you first need to acquire two tokens:
1. An OpenAI key from [here](https://platform.openai.com/api-keys), with access to GPT-4 (or a key for other [language models](https://pr-agent-docs.codium.ai/usage-guide/changing_a_model/), if you prefer).
1. An OpenAI key from [here](https://platform.openai.com/api-keys), with access to GPT-4 (or a key for other [language models](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/), if you prefer).
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 self-hosted PR-Agent:
There are several ways to use self-hosted Qodo Merge:
- [Locally](./locally.md)
- [GitHub](./github.md)
@ -14,8 +14,8 @@ There are several ways to use self-hosted PR-Agent:
- [BitBucket](./bitbucket.md)
- [Azure DevOps](./azure.md)
## PR-Agent Pro 💎
PR-Agent Pro, an app hosted by CodiumAI for GitHub\GitLab\BitBucket, is also available.
## Qodo Merge Pro 💎
Qodo Merge Pro, an app hosted by CodiumAI for GitHub\GitLab\BitBucket, is also available.
<br>
With PR-Agent Pro, installation is as simple as signing up and adding the PR-Agent app to your relevant repo.
See [here](https://pr-agent-docs.codium.ai/installation/pr_agent_pro/) for more details.
With Qodo Merge Pro, installation is as simple as signing up and adding the Qodo Merge app to your relevant repo.
See [here](https://qodo-merge-docs.qodo.ai/installation/pr_agent_pro/) for more details.

View File

@ -1,33 +1,33 @@
## Getting Started with PR-Agent Pro
## Getting Started with Qodo Merge Pro
PR-Agent Pro is a versatile application compatible with GitHub, GitLab, and BitBucket, hosted by CodiumAI.
See [here](https://pr-agent-docs.codium.ai/#pr-agent-pro) for more details about the benefits of using PR-Agent Pro.
Qodo Merge Pro is a versatile application compatible with GitHub, GitLab, and BitBucket, hosted by CodiumAI.
See [here](https://qodo-merge-docs.qodo.ai/overview/pr_agent_pro/) for more details about the benefits of using Qodo Merge Pro.
Interested parties can subscribe to PR-Agent Pro through the following [link](https://www.codium.ai/pricing/).
Interested parties can subscribe to Qodo Merge Pro through the following [link](https://www.codium.ai/pricing/).
After subscribing, you are granted the ability to easily install the application across any of your repositories.
![PR Agent Pro](https://codium.ai/images/pr_agent/pr_agent_pro_install.png){width=468}
![Qodo Merge Pro](https://codium.ai/images/pr_agent/pr_agent_pro_install.png){width=468}
Each user who wants to use PR-Agent pro needs to buy a seat.
Each user who wants to use Qodo Merge pro needs to buy a seat.
Initially, CodiumAI offers a two-week trial period at no cost, after which continued access requires each user to secure a personal seat.
Once a user acquires a seat, they gain the flexibility to use PR-Agent Pro across any repository where it was enabled.
Once a user acquires a seat, they gain the flexibility to use Qodo Merge Pro across any repository where it was enabled.
Users without a purchased seat who interact with a repository featuring PR-Agent Pro are entitled to receive up to five complimentary feedbacks.
Beyond this limit, PR-Agent Pro will cease to respond to their inquiries unless a seat is purchased.
Users without a purchased seat who interact with a repository featuring Qodo Merge Pro are entitled to receive up to five complimentary feedbacks.
Beyond this limit, Qodo Merge Pro will cease to respond to their inquiries unless a seat is purchased.
## Install PR-Agent Pro for GitHub Enterprise Server
You can install PR-Agent Pro application on your GitHub Enterprise Server, and enjoy two weeks of free trial.
After the trial period, to continue using PR-Agent Pro, you will need to contact us for an [Enterprise license](https://www.codium.ai/pricing/).
## Install Qodo Merge Pro for GitHub Enterprise Server
You can install Qodo Merge Pro application on your GitHub Enterprise Server, and enjoy two weeks of free trial.
After the trial period, to continue using Qodo Merge Pro, you will need to contact us for an [Enterprise license](https://www.codium.ai/pricing/).
## Install PR-Agent Pro for GitLab (Teams & Enterprise)
## Install Qodo Merge Pro for GitLab (Teams & Enterprise)
Since GitLab platform does not support apps, installing PR-Agent Pro for GitLab is a bit more involved, and requires the following steps:
Since GitLab platform does not support apps, installing Qodo Merge Pro for GitLab is a bit more involved, and requires the following steps:
### Step 1
Acquire a personal, project or group level access token. Enable the “api” scope in order to allow PR-Agent to read pull requests, comment and respond to requests.
Acquire a personal, project or group level access token. Enable the “api” scope in order to allow Qodo Merge to read pull requests, comment and respond to requests.
<figure markdown="1">
![Step 1](https://www.codium.ai/images/pr_agent/gitlab_pro_pat.png){width=750}
@ -65,4 +65,4 @@ Enable SSL verification: Check the box.
Youre all set!
Open a new merge request or add a MR comment with one of PR-Agents commands such as /review, /describe or /improve.
Open a new merge request or add a MR comment with one of Qodo Merges commands such as /review, /describe or /improve.

View File

@ -1,16 +1,16 @@
## Self-hosted PR-Agent
## Self-hosted Qodo Merge
- If you self-host PR-Agent with your OpenAI (or other LLM provider) API key, it is between you and the provider. We don't send your code data to PR-Agent servers.
- If you self-host Qodo Merge with your OpenAI (or other LLM provider) API key, it is between you and the provider. We don't send your code data to Qodo Merge servers.
## PR-Agent Pro 💎
## Qodo Merge Pro 💎
- 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.
- When using Qodo Merge 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.
- For certain clients, CodiumAI-hosted PR-Agent Pro will use CodiumAIs proprietary models. If this is the case, you will be notified.
- For certain clients, CodiumAI-hosted Qodo Merge Pro will use CodiumAIs proprietary models. If this is the case, you will be notified.
- No passive collection of Code and Pull Requests data — PR-Agent will be active only when you invoke it, and it will then extract and analyze only data relevant to the executed command and queried pull request.
- No passive collection of Code and Pull Requests data — Qodo Merge will be active only when you invoke it, and it will then extract and analyze only data relevant to the executed command and queried pull request.
## PR-Agent Chrome extension
## Qodo Merge Chrome extension
- The [PR-Agent Chrome extension](https://chromewebstore.google.com/detail/pr-agent-chrome-extension/ephlnjeghhogofkifjloamocljapahnl) will not send your code to any external servers.
- The [Qodo Merge Chrome extension](https://chromewebstore.google.com/detail/pr-agent-chrome-extension/ephlnjeghhogofkifjloamocljapahnl) will not send your code to any external servers.

View File

@ -1,25 +1,38 @@
# Overview
CodiumAI PR-Agent is an open-source tool to help efficiently review and handle pull requests.
Qodo Merge is an open-source tool to help efficiently review and handle pull requests.
- See the [Installation Guide](./installation/index.md) for instructions on installing and running the tool on different git platforms.
- See the [Usage Guide](./usage-guide/index.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 [Usage Guide](./usage-guide/index.md) for instructions on running the Qodo Merge commands via different interfaces, including _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened.
- See the [Tools Guide](./tools/index.md) for a detailed description of the different tools.
## PR-Agent Features
PR-Agent offers extensive pull request functionalities across various git providers.
## Qodo Merge Docs Smart Search
To search the documentation site using natural language:
1) Comment `/help "your question"` in either:
- A pull request where Qodo Merge is installed
- A [PR Chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat)
2) Qodo Merge will respond with an [answer](https://github.com/Codium-ai/pr-agent/pull/1241#issuecomment-2365259334) that includes relevant documentation links.
## Qodo Merge Features
Qodo Merge offers extensive pull request functionalities across various git providers.
| | | GitHub | Gitlab | Bitbucket | Azure DevOps |
|-------|-----------------------------------------------------------------------------------------------------------------------|:------:|:------:|:---------:|:------------:|
| TOOLS | Review | ✅ | ✅ | ✅ | ✅ |
| | ⮑ Incremental | ✅ | | | |
| | ⮑ [SOC2 Compliance](https://pr-agent-docs.codium.ai/tools/review/#soc2-ticket-compliance){:target="_blank"} 💎 | ✅ | ✅ | ✅ | ✅ |
| | ⮑ [SOC2 Compliance](https://qodo-merge-docs.qodo.ai/tools/review/#soc2-ticket-compliance){:target="_blank"} 💎 | ✅ | ✅ | ✅ | ✅ |
| | Ask | ✅ | ✅ | ✅ | ✅ |
| | Describe | ✅ | ✅ | ✅ | ✅ |
| | ⮑ [Inline file summary](https://pr-agent-docs.codium.ai/tools/describe/#inline-file-summary){:target="_blank"} 💎 | ✅ | ✅ | | ✅ |
| | ⮑ [Inline file summary](https://qodo-merge-docs.qodo.ai/tools/describe/#inline-file-summary){:target="_blank"} 💎 | ✅ | ✅ | | ✅ |
| | Improve | ✅ | ✅ | ✅ | ✅ |
| | ⮑ Extended | ✅ | ✅ | ✅ | ✅ |
| | [Custom Prompt](./tools/custom_prompt.md){:target="_blank"} 💎 | ✅ | ✅ | ✅ | ✅ |
@ -42,7 +55,7 @@ PR-Agent offers extensive pull request functionalities across various git provid
| | [Static code analysis](./tools/analyze.md/){:target="_blank"} 💎 | ✅ | ✅ | ✅ | ✅ |
| | [Multiple configuration options](./usage-guide/configuration_options.md){:target="_blank"} 💎 | ✅ | ✅ | ✅ | ✅ |
💎 marks a feature available only in [PR-Agent Pro](https://www.codium.ai/pricing/){:target="_blank"}
💎 marks a feature available only in [Qodo Merge Pro](https://www.codium.ai/pricing/){:target="_blank"}
## Example Results
@ -74,8 +87,8 @@ PR-Agent offers extensive pull request functionalities across various git provid
## How it Works
The following diagram illustrates PR-Agent tools and their flow:
The following diagram illustrates Qodo Merge tools and their flow:
![PR-Agent Tools](https://codium.ai/images/pr_agent/diagram-v0.9.png)
![Qodo Merge Tools](https://codium.ai/images/pr_agent/diagram-v0.9.png)
Check out the [PR Compression strategy](core-abilities/index.md) page for more details on how we convert a code diff to a manageable LLM prompt

View File

@ -1,42 +1,52 @@
[PR-Agent Pro](https://www.codium.ai/pricing/) is a hosted version of PR-Agent, provided by CodiumAI. A complimentary two-week trial is offered, followed by a monthly subscription fee.
PR-Agent Pro is designed for companies and teams that require additional features and capabilities. It provides the following benefits:
### Overview
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\GitLab\BitBucket repo.
[Qodo Merge Pro](https://www.codium.ai/pricing/) is a hosted version of Qodo Merge, provided by Qodo. A complimentary two-week trial is offered, followed by a monthly subscription fee.
Qodo Merge Pro is designed for companies and teams that require additional features and capabilities. It provides the following benefits:
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.
1. **Fully managed** - We take care of everything for you - hosting, models, regular updates, and more. Installation is as simple as signing up and adding the Qodo Merge app to your GitHub\GitLab\BitBucket repo.
3. **Improved support** - PR-Agent Pro users will receive priority support, and will be able to request new features and capabilities.
2. **Improved privacy** - No data will be stored or used to train models. Qodo Merge Pro will employ zero data retention, and will use an OpenAI and Claude accounts with zero data retention.
4. **Supporting self-hosted git servers** - PR-Agent Pro can be installed on GitHub Enterprise Server, GitLab, and BitBucket. For more information, see the [installation guide](https://pr-agent-docs.codium.ai/installation/pr_agent_pro/).
3. **Improved support** - Qodo Merge Pro users will receive priority support, and will be able to request new features and capabilities.
**Additional features:**
4. **Supporting self-hosted git servers** - Qodo Merge Pro can be installed on GitHub Enterprise Server, GitLab, and BitBucket. For more information, see the [installation guide](https://qodo-merge-docs.qodo.ai/installation/pr_agent_pro/).
Here are some of the additional features and capabilities that PR-Agent Pro offers:
5. **PR Chat** - Qodo Merge Pro allows you to engage in [private chat](https://qodo-merge-docs.qodo.ai/chrome-extension/features/#pr-chat) about your pull requests on private repositories.
### Additional features
Here are some of the additional features and capabilities that Qodo Merge Pro offers:
| Feature | Description |
|----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [**Model selection**](https://pr-agent-docs.codium.ai/usage-guide/PR_agent_pro_models/#pr-agent-pro-models) | Choose the model that best fits your needs, among top models like `GPT4` and `Claude-Sonnet-3.5`
| [**Global and wiki configuration**](https://pr-agent-docs.codium.ai/usage-guide/configuration_options/) | Control configurations for many repositories from a single location; <br>Edit configuration of a single repo without commiting code |
| [**Apply suggestions**](https://pr-agent-docs.codium.ai/tools/improve/#overview) | Generate commitable code from the relevant suggestions interactively by clicking on a checkbox |
| [**Suggestions impact**](https://pr-agent-docs.codium.ai/tools/improve/#assessing-impact) | Automatically mark suggestions that were implemented by the user (either directly in GitHub, or indirectly in the IDE) to enable tracking of the impact of the suggestions |
| [**CI feedback**](https://pr-agent-docs.codium.ai/tools/ci_feedback/) | Automatically analyze failed CI checks on GitHub and provide actionable feedback in the PR conversation, helping to resolve issues quickly |
| [**Advanced usage statistics**](https://www.codium.ai/contact/#/) | PR-Agent Pro offers detailed statistics at user, repository, and company levels, including metrics about PR-Agent usage, and also general statistics and insights |
| [**Incorporating companies' best practices**](https://pr-agent-docs.codium.ai/tools/improve/#best-practices) | Use the companies' best practices as reference to increase the effectiveness and the relevance of the code suggestions |
| [**Interactive triggering**](https://pr-agent-docs.codium.ai/tools/analyze/#example-usage) | Interactively apply different tools via the `analyze` command |
| [**SOC2 compliance check**](https://pr-agent-docs.codium.ai/tools/review/#configuration-options) | Ensures the PR contains a ticket to a project management system (e.g., Jira, Asana, Trello, etc.)
| [**Custom labels**](https://pr-agent-docs.codium.ai/tools/describe/#handle-custom-labels-from-the-repos-labels-page) | Define custom labels for PR-Agent to assign to the PR |
| [**Model selection**](https://qodo-merge-docs.qodo.ai/usage-guide/PR_agent_pro_models/) | Choose the model that best fits your needs, among top models like `GPT4` and `Claude-Sonnet-3.5`
| [**Global and wiki configuration**](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) | Control configurations for many repositories from a single location; <br>Edit configuration of a single repo without commiting code |
| [**Apply suggestions**](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) | Generate commitable code from the relevant suggestions interactively by clicking on a checkbox |
| [**Suggestions impact**](https://qodo-merge-docs.qodo.ai/tools/improve/#assessing-impact) | Automatically mark suggestions that were implemented by the user (either directly in GitHub, or indirectly in the IDE) to enable tracking of the impact of the suggestions |
| [**CI feedback**](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) | Automatically analyze failed CI checks on GitHub and provide actionable feedback in the PR conversation, helping to resolve issues quickly |
| [**Advanced usage statistics**](https://www.codium.ai/contact/#/) | Qodo Merge Pro offers detailed statistics at user, repository, and company levels, including metrics about Qodo Merge usage, and also general statistics and insights |
| [**Incorporating companies' best practices**](https://qodo-merge-docs.qodo.ai/tools/improve/#best-practices) | Use the companies' best practices as reference to increase the effectiveness and the relevance of the code suggestions |
| [**Interactive triggering**](https://qodo-merge-docs.qodo.ai/tools/analyze/#example-usage) | Interactively apply different tools via the `analyze` command |
| [**SOC2 compliance check**](https://qodo-merge-docs.qodo.ai/tools/review/#configuration-options) | Ensures the PR contains a ticket to a project management system (e.g., Jira, Asana, Trello, etc.)
| [**Custom labels**](https://qodo-merge-docs.qodo.ai/tools/describe/#handle-custom-labels-from-the-repos-labels-page) | Define custom labels for Qodo Merge to assign to the PR |
**Additional tools:**
### Additional tools
Here are additional tools that are available only for PR-Agent Pro users:
Here are additional tools that are available only for Qodo Merge Pro users:
| Feature | Description |
|---------|-------------|
| [**Custom Prompt Suggestions**](https://pr-agent-docs.codium.ai/tools/custom_prompt/) | Generate code suggestions based on custom prompts from the user |
| [**Analyze PR components**](https://pr-agent-docs.codium.ai/tools/analyze/) | Identify the components that changed in the PR, and enable to interactively apply different tools to them |
| [**Tests**](https://pr-agent-docs.codium.ai/tools/test/) | Generate tests for code components that changed in the PR |
| [**PR documentation**](https://pr-agent-docs.codium.ai/tools/documentation/) | Generate docstring for code components that changed in the PR |
| [**Improve Component**](https://pr-agent-docs.codium.ai/tools/improve_component/) | Generate code suggestions for code components that changed in the PR |
| [**Similar code search**](https://pr-agent-docs.codium.ai/tools/similar_code/) | Search for similar code in the repository, organization, or entire GitHub |
| [**Custom Prompt Suggestions**](https://qodo-merge-docs.qodo.ai/tools/custom_prompt/) | Generate code suggestions based on custom prompts from the user |
| [**Analyze PR components**](https://qodo-merge-docs.qodo.ai/tools/analyze/) | Identify the components that changed in the PR, and enable to interactively apply different tools to them |
| [**Tests**](https://qodo-merge-docs.qodo.ai/tools/test/) | Generate tests for code components that changed in the PR |
| [**PR documentation**](https://qodo-merge-docs.qodo.ai/tools/documentation/) | Generate docstring for code components that changed in the PR |
| [**Improve Component**](https://qodo-merge-docs.qodo.ai/tools/improve_component/) | Generate code suggestions for code components that changed in the PR |
| [**Similar code search**](https://qodo-merge-docs.qodo.ai/tools/similar_code/) | Search for similar code in the repository, organization, or entire GitHub |
### Supported languages
Qodo Merge Pro leverages the world's leading code models - Claude 3.5 Sonnet and GPT-4.
As a result, its primary tools such as `describe`, `review`, and `improve`, as well as the PR-chat feature, support virtually all programming languages.
For specialized commands that require static code analysis, Qodo Merge Pro offers support for specific languages. For more details about features that require static code analysis, please refer to the [documentation](https://qodo-merge-docs.qodo.ai/tools/analyze/#overview).

View File

@ -1,7 +1,7 @@
## Overview
The `analyze` tool combines advanced static code analysis with LLM capabilities to provide a comprehensive analysis of the PR code changes.
The tool scans the PR code changes, find the code components (methods, functions, classes) that changed, and enables to interactively generate tests, docs, code suggestions and similar code search for each component.
The tool scans the PR code changes, finds the code components (methods, functions, classes) that changed, and enables to interactively generate tests, docs, code suggestions and similar code search for each component.
It can be invoked manually by commenting on any PR:
```

View File

@ -28,7 +28,7 @@ When working from CLI, you need to apply the [configuration changes](#configurat
To enable custom labels, you need to apply the [configuration changes](#configuration-options) 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
> This feature is available only in Qodo Merge 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.

View File

@ -25,7 +25,7 @@ If you want to edit [configurations](#configuration-options), add the relevant o
### Automatic triggering
To run the `describe` automatically when a PR is opened, define in a [configuration file](https://pr-agent-docs.codium.ai/usage-guide/configuration_options/#wiki-configuration-file):
To run the `describe` automatically when a PR is opened, define in a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file):
```
[github_app]
pr_commands = [
@ -157,7 +157,7 @@ The marker `pr_agent:type` will be replaced with the PR type, `pr_agent:summary`
The default labels of the describe tool are quite generic, since they are meant to be used in any repo: [`Bug fix`, `Tests`, `Enhancement`, `Documentation`, `Other`].
You can define custom labels that are relevant for your repo and use cases.
Custom labels can be defined in a [configuration file](https://pr-agent-docs.codium.ai/tools/custom_labels/#configuration-options), or directly in the repo's [labels page](#handle-custom-labels-from-the-repos-labels-page).
Custom labels can be defined in a [configuration file](https://qodo-merge-docs.qodo.ai/tools/custom_labels/#configuration-options), or directly in the repo's [labels page](#handle-custom-labels-from-the-repos-labels-page).
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.
Each label description should be a **conditional statement**, that indicates if to add the label to the PR or not, according to the PR content.
@ -205,7 +205,7 @@ The description should be comprehensive and detailed, indicating when to add the
## Usage Tips
!!! tip "Automation"
- When you first install PR-Agent app, the [default mode](../usage-guide/automations_and_usage.md#github-app) for the describe tool is:
- When you first install Qodo Merge app, the [default mode](../usage-guide/automations_and_usage.md#github-app) for the describe tool is:
```
pr_commands = ["/describe", ...]
```

View File

@ -1,6 +1,6 @@
## Overview
The `help` tool provides a list of all the available tools and their descriptions.
For PR-Agent Pro users, it also enables to trigger each tool by checking the relevant box.
For Qodo Merge Pro users, it also enables to trigger each tool by checking the relevant box.
It can be invoked manually by commenting on any PR:
```

View File

@ -1,7 +1,7 @@
## Overview
The `improve` tool scans the PR code changes, and automatically generates [meaningful](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/pr_code_suggestions_prompts.toml#L41) suggestions for improving the PR code.
The tool can be triggered automatically every time a new PR is [opened](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened), or it can be invoked manually by commenting on any PR:
```
```toml
/improve
```
@ -9,7 +9,7 @@ The tool can be triggered automatically every time a new PR is [opened](../usage
![code_suggestions_as_comment_open.png](https://codium.ai/images/pr_agent/code_suggestions_as_comment_open.png){width=512}
Note that the `Apply this suggestion` checkbox, which interactively converts a suggestion into a commitable code comment, is available only for PR-Agent Pro 💎 users.
Note that the `Apply this suggestion` checkbox, which interactively converts a suggestion into a commitable code comment, is available only for Qodo Merge Pro 💎 users.
## Example usage
@ -19,12 +19,12 @@ Note that the `Apply this suggestion` checkbox, which interactively converts a s
Invoke the tool manually by commenting `/improve` on any PR. The code suggestions by default are presented as a single comment:
To edit [configurations](#configuration-options) related to the improve tool, use the following template:
```
```toml
/improve --pr_code_suggestions.some_config1=... --pr_code_suggestions.some_config2=...
```
For example, you can choose to present all the suggestions as commitable code comments, by running the following command:
```
```toml
/improve --pr_code_suggestions.commitable_code_suggestions=true
```
@ -36,8 +36,8 @@ Also note that collapsible are not supported in _Bitbucket_. Hence, the suggesti
### Automatic triggering
To run the `improve` automatically when a PR is opened, define in a [configuration file](https://pr-agent-docs.codium.ai/usage-guide/configuration_options/#wiki-configuration-file):
```
To run the `improve` automatically when a PR is opened, define in a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file):
```toml
[github_app]
pr_commands = [
"/improve",
@ -54,14 +54,14 @@ num_code_suggestions_per_chunk = ...
### Assessing Impact 💎
Note that PR-Agent pro tracks two types of implementations:
Note that Qodo Merge pro tracks two types of implementations:
- Direct implementation - when the user directly applies the suggestion by clicking the `Apply` checkbox.
- Indirect implementation - when the user implements the suggestion in their IDE environment. In this case, PR-Agent will utilize, after each commit, a dedicated logic to identify if a suggestion was implemented, and will mark it as implemented.
- Indirect implementation - when the user implements the suggestion in their IDE environment. In this case, Qodo Merge will utilize, after each commit, a dedicated logic to identify if a suggestion was implemented, and will mark it as implemented.
![code_suggestions_asses_impact](https://codium.ai/images/pr_agent/code_suggestions_asses_impact.png){width=512}
In post-process, PR-Agent counts the number of suggestions that were implemented, and provides general statistics and insights about the suggestions' impact on the PR process.
In post-process, Qodo Merge counts the number of suggestions that were implemented, and provides general statistics and insights about the suggestions' impact on the PR process.
![code_suggestions_asses_impact_stats_1](https://codium.ai/images/pr_agent/code_suggestions_asses_impact_stats_1.png){width=512}
@ -70,15 +70,43 @@ In post-process, PR-Agent counts the number of suggestions that were implemented
## Usage Tips
### Implementing the proposed code suggestions
Each generated suggestion consists of three key elements:
1. A single-line summary of the proposed change
2. An expandable section containing a comprehensive description of the suggestion
3. A diff snippet showing the recommended code modification (before and after)
We advise users to apply critical analysis and judgment when implementing the proposed suggestions.
In addition to mistakes (which may happen, but are rare), sometimes the presented code modification may serve more as an _illustrative example_ than a direct applicable solution.
In such cases, we recommend prioritizing the suggestion's detailed description, using the diff snippet primarily as a supporting reference.
### Dual publishing mode
Our recommended approach for presenting code suggestions is through a [table](https://qodo-merge-docs.qodo.ai/tools/improve/#overview) (`--pr_code_suggestions.commitable_code_suggestions=false`).
This method significantly reduces the PR footprint and allows for quick and easy digestion of multiple suggestions.
We also offer a complementary **dual publishing mode**. When enabled, suggestions exceeding a certain score threshold are not only displayed in the table, but also presented as commitable PR comments.
This mode helps highlight suggestions deemed more critical.
To activate dual publishing mode, use the following setting:
```toml
[pr_code_suggestions]
dual_publishing_score_threshold = x
```
Where x represents the minimum score threshold (>=) for suggestions to be presented as commitable PR comments in addition to the table. Default is -1 (disabled).
### Self-review
If you set in a configuration file:
```
```toml
[pr_code_suggestions]
demand_code_suggestions_self_review = true
```
The `improve` tool will add a checkbox below the suggestions, prompting user to acknowledge that they have reviewed the suggestions.
You can set the content of the checkbox text via:
```
```toml
[pr_code_suggestions]
code_suggestions_self_review_text = "... (your text here) ..."
```
@ -86,26 +114,60 @@ code_suggestions_self_review_text = "... (your text here) ..."
![self_review_1](https://codium.ai/images/pr_agent/self_review_1.png){width=512}
💎 In addition, by setting:
```
[pr_code_suggestions]
approve_pr_on_self_review = true
```
the tool can automatically approve the PR when the user checks the self-review checkbox.
!!! tip "Tip - demanding self-review from the PR author"
If you set the number of required reviewers for a PR to 2, this effectively means that the PR author must click the self-review checkbox before the PR can be merged (in addition to a human reviewer).
!!! tip "Tip - demanding self-review from the PR author 💎"
By setting:
```toml
[pr_code_suggestions]
approve_pr_on_self_review = true
```
the tool can automatically add an approval when the PR author clicks the self-review checkbox.
- If you set the number of required reviewers for a PR to 2, this effectively means that the PR author must click the self-review checkbox before the PR can be merged (in addition to a human reviewer).
![self_review_2](https://codium.ai/images/pr_agent/self_review_2.png){width=512}
### `Extra instructions` and `best practices`
- If you keep the number of required reviewers for a PR to 1 and enable this configuration, this effectively means that the PR author can approve the PR by actively clicking the self-review checkbox.
To prevent unauthorized approvals, this configuration defaults to false, and cannot be altered through online comments; enabling requires a direct update to the configuration file and a commit to the repository. This ensures that utilizing the feature demands a deliberate documented decision by the repository owner.
### How many code suggestions are generated?
Qodo Merge uses a dynamic strategy to generate code suggestions based on the size of the pull request (PR). Here's how it works:
1) Chunking large PRs:
- Qodo Merge divides large PRs into 'chunks'.
- Each chunk contains up to `pr_code_suggestions.max_context_tokens` tokens (default: 14,000).
2) Generating suggestions:
- For each chunk, Qodo Merge generates up to `pr_code_suggestions.num_code_suggestions_per_chunk` suggestions (default: 4).
This approach has two main benefits:
- Scalability: The number of suggestions scales with the PR size, rather than being fixed.
- Quality: By processing smaller chunks, the AI can maintain higher quality suggestions, as larger contexts tend to decrease AI performance.
Note: Chunking is primarily relevant for large PRs. For most PRs (up to 500 lines of code), Qodo Merge will be able to process the entire code in a single call.
### 'Extra instructions' and 'best practices'
#### Extra instructions
>`Platforms supported: GitHub, GitLab, Bitbucket`
You can use the `extra_instructions` configuration option to give the AI model additional instructions for the `improve` tool.
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 possible instructions:
```
```toml
[pr_code_suggestions]
extra_instructions="""\
(1) Answer in japanese
@ -117,10 +179,13 @@ extra_instructions="""\
Use triple quotes to write multi-line instructions. Use bullet points or numbers to make the instructions more readable.
#### Best practices 💎
Another option to give additional guidance to the AI model is by creating a dedicated [**wiki page**](https://github.com/Codium-ai/pr-agent/wiki) called `best_practices.md`.
This page can contain a list of best practices, coding standards, and guidelines that are specific to your repo/organization
The AI model will use this page as a reference, and in case the PR code violates any of the guidelines, it will suggest improvements accordingly, with a dedicated label: `Organization
>`Platforms supported: GitHub, GitLab`
Another option to give additional guidance to the AI model is by creating a dedicated [**wiki page**](https://github.com/Codium-ai/pr-agent/wiki) called `best_practices.md`.
This page can contain a list of best practices, coding standards, and guidelines that are specific to your repo/organization.
The AI model will use this wiki page as a reference, and in case the PR code violates any of the guidelines, it will suggest improvements accordingly, with a dedicated label: `Organization
best practice`.
Example for a `best_practices.md` content can be found [here](https://github.com/Codium-ai/pr-agent/blob/main/docs/docs/usage-guide/EXAMPLE_BEST_PRACTICE.md) (adapted from Google's [pyguide](https://google.github.io/styleguide/pyguide.html)).
@ -128,93 +193,109 @@ This file is only an example. Since it is used as a prompt for an AI model, we w
- It should be written in a clear and concise manner
- If needed, it should give short relevant code snippets as examples
- Up to 800 lines are allowed
- Recommended to limit the text to 800 lines or fewer. Heres why:
1) Extremely long best practices documents may not be fully processed by the AI model.
2) A lengthy file probably represent a more "**generic**" set of guidelines, which the AI model is already familiar with. The objective is to focus on a more targeted set of guidelines tailored to the specific needs of this project.
Example results:
##### Local and global best practices
By default, Qodo Merge will look for a local `best_practices.md` wiki file in the root of the relevant local repo.
If you want to enable also a global `best_practices.md` wiki file, set first in the global configuration file:
```toml
[best_practices]
enable_global_best_practices = true
```
Then, create a `best_practices.md` wiki file in the root of [global](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file) configuration repository, `pr-agent-settings`.
##### Example results
![best_practice](https://codium.ai/images/pr_agent/org_best_practice.png){width=512}
Note that while the `extra instructions` are more related to the way the `improve` tool behaves, the `best_practices.md` file is a general guideline for the way code should be written in the repo.
#### How to combine `extra instructions` and `best practices`
The `extra instructions` configuration is more related to the `improve` tool prompt. It can be used, for example, to avoid specific suggestions ("Don't suggest to add try-except block", "Ignore changes in toml files", ...) or to emphasize specific aspects or formats ("Answer in Japanese", "Give only short suggestions", ...)
In contrast, the `best_practices.md` file is a general guideline for the way code should be written in the repo.
Using a combination of both can help the AI model to provide relevant and tailored suggestions.
## Configuration options
!!! example "General options"
??? example "General options"
<table>
<tr>
<td><b>extra_instructions</b></td>
<td>Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ...".</td>
</tr>
<tr>
<td><b>commitable_code_suggestions</b></td>
<td>If set to true, the tool will display the suggestions as commitable code comments. Default is false.</td>
</tr>
<tr>
<td><b>dual_publishing_score_threshold</b></td>
<td>Minimum score threshold for suggestions to be presented as commitable PR comments in addition to the table. Default is -1 (disabled).</td>
</tr>
<tr>
<td><b>persistent_comment</b></td>
<td>If set to true, the improve comment will be persistent, meaning that every new improve request will edit the previous one. Default is false.</td>
</tr>
<tr>
<td><b>self_reflect_on_suggestions</b></td>
<td>If set to true, the improve tool will calculate an importance score for each suggestion [1-10], and sort the suggestion labels group based on this score. Default is true.</td>
</tr>
<tr>
<td><b>suggestions_score_threshold</b></td>
<td> Any suggestion with importance score less than this threshold will be removed. Default is 0. Highly recommend not to set this value above 7-8, since above it may clip relevant suggestions that can be useful. </td>
</tr>
<tr>
<td><b>apply_suggestions_checkbox</b></td>
<td> Enable the checkbox to create a committable suggestion. Default is true.</td>
</tr>
<tr>
<td><b>enable_help_text</b></td>
<td>If set to true, the tool will display a help text in the comment. Default is true.</td>
</tr>
<tr>
<td><b>enable_chat_text</b></td>
<td>If set to true, the tool will display a reference to the PR chat in the comment. Default is true.</td>
</tr>
</table>
<table>
<tr>
<td><b>num_code_suggestions</b></td>
<td>Number of code suggestions provided by the 'improve' tool. Default is 4 for CLI, 0 for auto tools.</td>
</tr>
<tr>
<td><b>extra_instructions</b></td>
<td>Optional extra instructions to the tool. For example: "focus on the changes in the file X. Ignore change in ...".</td>
</tr>
<tr>
<td><b>rank_suggestions</b></td>
<td>If set to true, the tool will rank the suggestions, based on importance. Default is false.</td>
</tr>
<tr>
<td><b>commitable_code_suggestions</b></td>
<td>If set to true, the tool will display the suggestions as commitable code comments. Default is false.</td>
</tr>
<tr>
<td><b>persistent_comment</b></td>
<td>If set to true, the improve comment will be persistent, meaning that every new improve request will edit the previous one. Default is false.</td>
</tr>
<tr>
<td><b>self_reflect_on_suggestions</b></td>
<td>If set to true, the improve tool will calculate an importance score for each suggestion [1-10], and sort the suggestion labels group based on this score. Default is true.</td>
</tr>
<tr>
<td><b>suggestions_score_threshold</b></td>
<td> Any suggestion with importance score less than this threshold will be removed. Default is 0. Highly recommend not to set this value above 7-8, since above it may clip relevant suggestions that can be useful. </td>
</tr>
<tr>
<td><b>apply_suggestions_checkbox</b></td>
<td> Enable the checkbox to create a committable suggestion. Default is true.</td>
</tr>
<tr>
<td><b>enable_help_text</b></td>
<td>If set to true, the tool will display a help text in the comment. Default is true.</td>
</tr>
</table>
??? example "Params for number of suggestions and AI calls"
!!! example "params for 'extended' mode"
<table>
<tr>
<td><b>auto_extended_mode</b></td>
<td>Enable extended mode automatically (no need for the --extended option). Default is true.</td>
</tr>
<tr>
<td><b>num_code_suggestions_per_chunk</b></td>
<td>Number of code suggestions provided by the 'improve' tool, per chunk. Default is 5.</td>
</tr>
<tr>
<td><b>rank_extended_suggestions</b></td>
<td>If set to true, the tool will rank the suggestions, based on importance. Default is true.</td>
</tr>
<tr>
<td><b>max_number_of_calls</b></td>
<td>Maximum number of chunks. Default is 5.</td>
</tr>
<tr>
<td><b>final_clip_factor</b></td>
<td>Factor to remove suggestions with low confidence. Default is 0.9.</td>
</tr>
</table>
<table>
<tr>
<td><b>auto_extended_mode</b></td>
<td>Enable chunking the PR code and running the tool on each chunk. Default is true.</td>
</tr>
<tr>
<td><b>num_code_suggestions_per_chunk</b></td>
<td>Number of code suggestions provided by the 'improve' tool, per chunk. Default is 4.</td>
</tr>
<tr>
<td><b>max_number_of_calls</b></td>
<td>Maximum number of chunks. Default is 3.</td>
</tr>
<tr>
<td><b>rank_extended_suggestions</b></td>
<td>If set to true, the tool will rank the suggestions, based on importance. Default is true.</td>
</tr>
</table>
## 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. Critical reading and judgment are required.
- While mistakes of the AI are rare but can happen, a real benefit from the suggestions of the `improve` (and [`review`](https://pr-agent-docs.codium.ai/tools/review/)) tool is to catch, with high probability, **mistakes or bugs done by the PR author**, when they happen. So, it's a good practice to spend the needed ~30-60 seconds to review the suggestions, even if not all of them are always relevant.
- AI models for code are getting better and better (Sonnet-3.5 and GPT-4), but they are not flawless. Not all the suggestions will be perfect, and a user should not accept all of them automatically. Critical reading and judgment are required.
- While mistakes of the AI are rare but can happen, a real benefit from the suggestions of the `improve` (and [`review`](https://qodo-merge-docs.qodo.ai/tools/review/)) tool is to catch, with high probability, **mistakes or bugs done by the PR author**, when they happen. So, it's a good practice to spend the needed ~30-60 seconds to review the suggestions, even if not all of them are always relevant.
- The hierarchical structure of the suggestions is designed to help the user to _quickly_ understand them, and to decide which ones are relevant and which are not:
- Only if the `Category` header is relevant, the user should move to the summarized suggestion description
- Only if the summarized suggestion description is relevant, the user should click on the collapsible, to read the full suggestion description with a code preview example.
In addition, we recommend to use the `extra_instructions` field to guide the model to suggestions that are more relevant to the specific needs of the project.
<br>
Consider also trying the [Custom Prompt Tool](./custom_prompt.md) 💎, that will **only** propose code suggestions that follow specific guidelines defined by user.
- In addition, we recommend to use the [`extra_instructions`](https://qodo-merge-docs.qodo.ai/tools/improve/#extra-instructions-and-best-practices) field to guide the model to suggestions that are more relevant to the specific needs of the project.
- The interactive [PR chat](https://qodo-merge-docs.qodo.ai/chrome-extension/) also provides an easy way to get more tailored suggestions and feedback from the AI model.

View File

@ -1,6 +1,6 @@
# Tools
Here is a list of PR-Agent tools, each with a dedicated page that explains how to use it:
Here is a list of Qodo Merge tools, each with a dedicated page that explains how to use it:
| Tool | Description |
|------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
@ -19,4 +19,4 @@ Here is a list of PR-Agent tools, each with a dedicated page that explains how t
| **💎 [Improve Component (`/improve_component component_name`](./improve_component.md))** | Generates code suggestions for a specific code component that changed in the PR |
| **💎 [CI Feedback (`/checks ci_job`](./ci_feedback.md))** | Automatically generates feedback and analysis for a failed CI job |
Note that the tools marked with 💎 are available only for PR-Agent Pro users.
Note that the tools marked with 💎 are available only for Qodo Merge Pro users.

View File

@ -8,6 +8,9 @@ The tool can be triggered automatically every time a new PR is [opened](../usage
Note that the main purpose of the `review` tool is to provide the **PR reviewer** with useful feedbacks and insights. The PR author, in contrast, may prefer to save time and focus on the output of the [improve](./improve.md) tool, which provides actionable code suggestions.
(Read more about the different personas in the PR process and how Qodo Merge aims to assist them in our [blog](https://www.codium.ai/blog/understanding-the-challenges-and-pain-points-of-the-pull-request-cycle/))
## Example usage
### Manual triggering
@ -27,7 +30,7 @@ If you want to edit [configurations](#configuration-options), add the relevant o
### Automatic triggering
To run the `review` automatically when a PR is opened, define in a [configuration file](https://pr-agent-docs.codium.ai/usage-guide/configuration_options/#wiki-configuration-file):
To run the `review` automatically when a PR is opened, define in a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#wiki-configuration-file):
```
[github_app]
pr_commands = [
@ -46,7 +49,7 @@ num_code_suggestions = ...
[//]: # ()
[//]: # (### 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.)
[//]: # (Incremental review only considers changes since the last Qodo Merge 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:)
@ -195,7 +198,7 @@ If enabled, the `review` tool can approve a PR when a specific comment, `/review
On the other hand, if you find one of the enabled features to be irrelevant for your use case, disable it. No default configuration can fit all use cases.
!!! tip "Automation"
When you first install PR-Agent app, the [default mode](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened) for the `review` tool is:
When you first install Qodo Merge app, the [default mode](../usage-guide/automations_and_usage.md#github-app-automatic-tools-when-a-new-pr-is-opened) for the `review` tool is:
```
pr_commands = ["/review --pr_reviewer.num_code_suggestions=0", ...]
```
@ -234,7 +237,7 @@ If enabled, the `review` tool can approve a PR when a specific comment, `/review
!!! tip "Auto-approval"
PR-Agent can approve a PR when a specific comment is invoked.
Qodo Merge 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:
```
@ -248,7 +251,7 @@ If enabled, the `review` tool can approve a PR when a specific comment, `/review
```
/review auto_approve
```
PR-Agent will automatically approve the PR, and add a comment with the approval.
Qodo Merge 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:

View File

@ -8,9 +8,9 @@ For example:
![similar code global](https://codium.ai/images/pr_agent/similar_code_global2.png){width=768}
PR-Agent will examine the code component and will extract the most relevant keywords to search for similar code:
Qodo Merge will examine the code component and will extract the most relevant keywords to search for similar code:
- `extracted keywords`: the keywords that were extracted from the code by PR-Agent. the link will open a search page with the extracted keywords, to allow the user to modify the search if needed.
- `extracted keywords`: the keywords that were extracted from the code by Qodo Merge. the link will open a search page with the extracted keywords, to allow the user to modify the search if needed.
- `search context`: the context in which the search will be performed, organization's codebase or open-source code (Global).
- `similar code`: the most similar code components found. the link will open the code component in the relevant file.
- `relevant repositories`: the open-source repositories in which that are relevant to the searched code component and it's keywords.

View File

@ -10,14 +10,10 @@ To get a list of the components that changed in the PR and choose the relevant c
## Example usage
Invoke the tool manually by commenting `/test` on any PR:
![test1](https://codium.ai/images/pr_agent/test1.png){width=704}
The tool will generate tests for the selected component (if no component is stated, it will generate tests for largest component):
![test2](https://codium.ai/images/pr_agent/test2.png){width=768}
![test1](https://codium.ai/images/pr_agent/test1.png){width=768}
![test3](https://codium.ai/images/pr_agent/test3.png){width=768}
(Example taken from [here](https://github.com/Codium-ai/pr-agent/pull/598#issuecomment-1913679429)):

View File

@ -1,30 +1,18 @@
## PR-Agent Pro Models
## Qodo Merge Pro Models
The default models used by PR-Agent Pro are OpenAI's GPT-4 models. We use a combination of GPT-4-Turbo and GPT-4o to strike a balance between speed and quality.
The default models used by Qodo Merge Pro are a combination of Claude-3.5-sonnet and OpenAI's GPT-4 models.
However, users can change the model used by PR-Agent Pro to Claude-3.5-sonnet, which also excels at code tasks.
To do so, add the following to your [configuration](https://pr-agent-docs.codium.ai/usage-guide/configuration_options/) file:
Users can configure Qodo Merge Pro to use solely a specific model by editing the [configuration](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/) file.
For example, to restrict Qodo Merge Pro to using only `Claude-3.5-sonnet`, add this setting:
```
[config]
model="claude-3-5-sonnet"
```
Note that Claude models tend to give lower scores for each suggestion, so if you are using a [threshold](https://pr-agent-docs.codium.ai/tools/improve/#configuration-options):
Or to restrict Qodo Merge Pro to using only `GPT-4o`, add this setting:
```
[pr_code_suggestions]
suggestions_score_threshold=...
```
You might need to adjust this value when switching models.
### Dedicated models per tool
You can also use different models for different tools. For example, you can use the Claude-3.5-sonnet model only for the `improve` tool (and keep the default GPT-4 model for the other tools) by adding the following to your configuration file:
```
[github_app]
pr_commands = [
"/describe --pr_description.final_update_message=false",
"/review --pr_reviewer.num_code_suggestions=0",
"/improve --config.model=claude-3-5-sonnet",
]
[config]
model="gpt-4o"
```

View File

@ -1,6 +1,28 @@
## Show possible configurations
The possible configurations of Qodo Merge are stored in [here](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml).
In the [tools](https://qodo-merge-docs.qodo.ai/tools/) page you can find explanations on how to use these configurations for each tool.
To print all the available configurations as a comment on your PR, you can use the following command:
```
/config
```
![possible_config1](https://codium.ai/images/pr_agent/possible_config1.png){width=512}
To view the **actual** configurations used for a specific tool, after all the user settings are applied, you can add for each tool a `--config.output_relevant_configurations=true` suffix.
For example:
```
/improve --config.output_relevant_configurations=true
```
Will output an additional field showing the actual configurations used for the `improve` tool.
![possible_config2](https://codium.ai/images/pr_agent/possible_config2.png){width=512}
## Ignoring files from analysis
In some cases, you may want to exclude specific files or directories from the analysis performed by 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.
In some cases, you may want to exclude specific files or directories from the analysis performed by Qodo Merge. This can be useful, for example, when you have files that are generated automatically or files that shouldn't be reviewed, like vendor code.
You can ignore files or folders using the following methods:
- `IGNORE.GLOB`
@ -30,7 +52,7 @@ regex = ['.*\.py$']
## Extra instructions
All PR-Agent tools have a parameter called `extra_instructions`, that enables to add free-text extra instructions. Example usage:
All Qodo Merge tools have a parameter called `extra_instructions`, that enables to add free-text extra instructions. Example usage:
```
/update_changelog --pr_update_changelog.extra_instructions="Make sure to update also the version ..."
```
@ -42,9 +64,9 @@ This mode provides a very good speed-quality-cost tradeoff, and can handle most
When the PR is above the token limit, it employs a [PR Compression strategy](../core-abilities/index.md).
However, for very large PRs, or in case you want to emphasize quality over speed and cost, there are two possible solutions:
1) [Use a model](https://codium-ai.github.io/Docs-PR-Agent/usage-guide/#changing-a-model) with larger context, like GPT-32K, or claude-100K. This solution will be applicable for all the tools.
2) For the `/improve` tool, there is an ['extended' mode](https://codium-ai.github.io/Docs-PR-Agent/tools/#improve) (`/improve --extended`),
which divides the PR to chunks, and processes each chunk separately. With this mode, regardless of the model, no compression will be done (but for large PRs, multiple model calls may occur)
1) [Use a model](https://qodo-merge-docs.qodo.ai/usage-guide/changing_a_model/) with larger context, like GPT-32K, or claude-100K. This solution will be applicable for all the tools.
2) For the `/improve` tool, there is an ['extended' mode](https://qodo-merge-docs.qodo.ai/tools/improve/) (`/improve --extended`),
which divides the PR into chunks, and processes each chunk separately. With this mode, regardless of the model, no compression will be done (but for large PRs, multiple model calls may occur)
@ -63,20 +85,21 @@ By default, around any change in your PR, git patch provides three lines of cont
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:
Qodo Merge will try to increase the number of lines of context, via the parameter:
```
[config]
patch_extra_lines_before=4
patch_extra_lines_after=2
patch_extra_lines_before=3
patch_extra_lines_after=1
```
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](https://github.com/Codium-ai/pr-agent/blob/main/PR_COMPRESSION.md)), PR-Agent automatically sets this number to 0, using the original git patch.
Increasing this number provides more context to the model, but will also increase the token budget, and may overwhelm the model with too much information, unrelated to the actual PR code changes.
If the PR is too large (see [PR Compression strategy](https://github.com/Codium-ai/pr-agent/blob/main/PR_COMPRESSION.md)), Qodo Merge may automatically set this number to 0, and will use the original git patch.
## Editing the prompts
The prompts for the various PR-Agent tools are defined in the `pr_agent/settings` folder.
The prompts for the various Qodo Merge tools are defined in the `pr_agent/settings` folder.
In practice, the prompts are loaded and stored as a standard setting object.
Hence, editing them is similar to editing any other configuration value - just place the relevant key in `.pr_agent.toml`file, and override the default value.
@ -112,3 +135,28 @@ LANGSMITH_API_KEY=<api_key>
LANGSMITH_PROJECT=<project>
LANGSMITH_BASE_URL=<url>
```
## Ignoring automatic commands in PRs
In some cases, you may want to automatically ignore specific PRs . Qodo Merge enables you to ignore PR with a specific title, or from/to specific branches (regex matching).
To ignore PRs with a specific title such as "[Bump]: ...", you can add the following to your `configuration.toml` file:
```
[config]
ignore_pr_title = ["\\[Bump\\]"]
```
Where the `ignore_pr_title` is a list of regex patterns to match the PR title you want to ignore. Default is `ignore_pr_title = ["^\\[Auto\\]", "^Auto"]`.
To ignore PRs from specific source or target branches, you can add the following to your `configuration.toml` file:
```
[config]
ignore_pr_source_branches = ['develop', 'main', 'master', 'stage']
ignore_pr_target_branches = ["qa"]
```
Where the `ignore_pr_source_branches` and `ignore_pr_target_branches` are lists of regex patterns to match the source and target branches you want to ignore.
They are not mutually exclusive, you can use them together or separately.

View File

@ -1,5 +1,5 @@
## Local repo (CLI)
When running from your locally cloned PR-Agent repo (CLI), your local configuration file will be used.
When running from your locally cloned Qodo Merge 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`
@ -28,7 +28,7 @@ This is useful for debugging or experimenting with different tools.
(3)
**git provider**: The [git_provider](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L5) field in a configuration file determines the GIT provider that will be used by PR-Agent. Currently, the following providers are supported:
**git provider**: The [git_provider](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L5) field in a configuration file determines the GIT provider that will be used by Qodo Merge. Currently, the following providers are supported:
`
"github", "gitlab", "bitbucket", "azure", "codecommit", "local", "gerrit"
`
@ -39,7 +39,7 @@ Default is "github".
### 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.
Online usage means invoking Qodo Merge 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`
@ -60,8 +60,8 @@ Any configuration value in [configuration file](https://github.com/Codium-ai/pr-
## GitHub App
!!! note "Configurations for PR-Agent Pro"
PR-Agent Pro for GitHub is an App, hosted by CodiumAI. So all the instructions below are relevant also for PR-Agent Pro users.
!!! note "Configurations for Qodo Merge Pro"
Qodo Merge Pro for GitHub is an App, hosted by CodiumAI. So all the instructions below are relevant also for Qodo Merge Pro users.
Same goes for [GitLab webhook](#gitlab-webhook) and [BitBucket App](#bitbucket-app) sections.
### GitHub app automatic tools when a new PR is opened
@ -77,10 +77,10 @@ pr_commands = [
"/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.
This means that when a new PR is opened/reopened or marked as ready for review, Qodo Merge will run the `describe`, `review` and `improve` tools.
For the `review` tool, for example, the `num_code_suggestions` parameter will be set to 0.
You can override the default tool parameters by using one the three options for a [configuration file](https://codium-ai.github.io/Docs-PR-Agent/usage-guide/#configuration-options): **wiki**, **local**, or **global**.
You can override the default tool parameters by using one the three options for a [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/): **wiki**, **local**, or **global**.
For example, if your local `.pr_agent.toml` file contains:
```
[pr_description]
@ -94,13 +94,6 @@ To cancel the automatic run of all the tools, set:
pr_commands = []
```
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.
@ -115,10 +108,10 @@ push_commands = [
"/review --pr_reviewer.num_code_suggestions=0 --pr_reviewer.final_update_message=false",
]
```
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.
This means that when new code is pushed to the PR, the Qodo Merge will run the `describe` and `review` tools, with the specified parameters.
## GitHub Action
`GitHub Action` is a different way to trigger PR-Agent tools, and uses a different configuration mechanism than `GitHub App`.<br>
`GitHub Action` is a different way to trigger Qodo Merge tools, and uses a different configuration mechanism than `GitHub App`.<br>
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
@ -128,22 +121,26 @@ Specifically, start by setting the following environment variables:
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.pr_actions: ["opened", "reopened", "ready_for_review", "review_requested"]
```
`github_action_config.auto_review`, `github_action_config.auto_describe` and `github_action_config.auto_improve` are used to enable/disable automatic tools that run when a new PR is opened.
If not set, the default configuration is for all three tools to run automatically when a new PR is opened.
`github_action_config.pr_actions` is used to configure which `pull_requests` events will trigger the enabled auto flags
If not set, the default configuration is `["opened", "reopened", "ready_for_review", "review_requested"]`
`github_action_config.enable_output` are used to enable/disable github actions [output parameter](https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#outputs-for-docker-container-and-javascript-actions) (default is `true`).
Review result is output as JSON to `steps.{step-id}.outputs.review` property.
The JSON structure is equivalent to the yaml data structure defined in [pr_reviewer_prompts.toml](https://github.com/idubnori/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml).
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` [configuration file](https://pr-agent-docs.codium.ai/usage-guide/configuration_options/#global-configuration-file) in the root of your repo
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` [configuration file](https://qodo-merge-docs.qodo.ai/usage-guide/configuration_options/#global-configuration-file) in the root of your repo
For example, you can set an environment variable: `pr_description.publish_labels=false`, or add a `.pr_agent.toml` file with the following content:
```
[pr_description]
publish_labels = false
```
to prevent PR-Agent from publishing labels when running the `describe` tool.
to prevent Qodo Merge from publishing labels when running the `describe` tool.
## GitLab Webhook
After setting up a GitLab webhook, to control which commands will run automatically when a new MR is opened, you can set the `pr_commands` parameter in the configuration file, similar to the GitHub App:
@ -171,17 +168,23 @@ push_commands = [
Note that to use the 'handle_push_trigger' feature, you need to give the gitlab webhook also the "Push events" scope.
## BitBucket App
Similar to GitHub app, when running PR-Agent from BitBucket App, the default [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) from a pre-built docker will be initially loaded.
Similar to GitHub app, when running Qodo Merge from BitBucket App, the default [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) 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
extra_instructions = "Answer in japanese"
```
Each time you invoke a `/review` tool, it will use inline code comments.
Each time you invoke a `/review` tool, it will use the extra instructions you set in the local configuration file.
Note that among other limitations, BitBucket provides relatively low rate-limits for applications (up to 1000 requests per hour), and does not provide an API to track the actual rate-limit usage.
If you experience lack of responses from Qodo Merge, you might want to set: `bitbucket_app.avoid_full_files=true` in your configuration file.
This will prevent Qodo Merge from acquiring the full file content, and will only use the diff content. This will reduce the number of requests made to BitBucket, at the cost of small decrease in accuracy, as dynamic context will not be applicable.
### BitBucket Self-Hosted App automatic tools

View File

@ -1,7 +1,7 @@
The different tools and sub-tools used by CodiumAI PR-Agent are adjustable via the **[configuration file](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml)**.
The different tools and sub-tools used by Qodo Merge are adjustable via the **[configuration file](https://github.com/Codium-ai/pr-agent/blob/main/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](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml#L16) section in the configuration file.
See the [Tools Guide](https://codium-ai.github.io/Docs-PR-Agent/tools/) for a detailed description of the different tools and their configurations.
See the [Tools Guide](https://qodo-merge-docs.qodo.ai/tools/) for a detailed description of the different tools and their configurations.
There are three ways to set persistent configurations:
@ -20,7 +20,7 @@ In terms of precedence, wiki configurations will override local configurations,
`Platforms supported: GitHub, GitLab`
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.
With Qodo Merge 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**.
@ -34,7 +34,7 @@ An example content:
generate_ai_title=true
```
PR-Agent will know to remove the surrounding quotes when reading the configuration content.
Qodo Merge will know to remove the surrounding quotes when reading the configuration content.
## Local configuration file

View File

@ -1,7 +1,7 @@
# Usage guide
This page provides a detailed guide on how to use PR-Agent.
It includes information on how to adjust PR-Agent configurations, define which tools will run automatically, and other advanced configurations.
This page provides a detailed guide on how to use Qodo Merge.
It includes information on how to adjust Qodo Merge configurations, define which tools will run automatically, and other advanced configurations.
- [Introduction](./introduction.md)
@ -23,4 +23,4 @@ It includes information on how to adjust PR-Agent configurations, define which t
- [Changing a model](./additional_configurations.md#changing-a-model)
- [Patch Extra Lines](./additional_configurations.md#patch-extra-lines)
- [Editing the prompts](./additional_configurations.md#editing-the-prompts)
- [PR-Agent Pro Models](./PR_agent_pro_models.md)
- [Qodo Merge Pro Models](./PR_agent_pro_models.md)

View File

@ -1,13 +1,13 @@
After [installation](https://pr-agent-docs.codium.ai/installation/), there are three basic ways to invoke CodiumAI PR-Agent:
After [installation](https://qodo-merge-docs.qodo.ai/installation/), there are three basic ways to invoke Qodo Merge:
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
3. Enabling Qodo Merge tools to run automatically when a new PR is opened
Specifically, CLI commands can be issued by invoking a pre-built [docker image](https://pr-agent-docs.codium.ai/installation/locally/#using-docker-image), or by invoking a [locally cloned repo](https://pr-agent-docs.codium.ai/installation/locally/#run-from-source).
Specifically, CLI commands can be issued by invoking a pre-built [docker image](https://qodo-merge-docs.qodo.ai/installation/locally/#using-docker-image), or by invoking a [locally cloned repo](https://qodo-merge-docs.qodo.ai/installation/locally/#run-from-source).
For online usage, you will need to setup either a [GitHub App](https://pr-agent-docs.codium.ai/installation/github/#run-as-a-github-app) or a [GitHub Action](https://pr-agent-docs.codium.ai/installation/github/#run-as-a-github-action) (GitHub), a [GitLab webhook](https://pr-agent-docs.codium.ai/installation/gitlab/#run-a-gitlab-webhook-server) (GitLab), or a [BitBucket App](https://pr-agent-docs.codium.ai/installation/bitbucket/#run-using-codiumai-hosted-bitbucket-app) (BitBucket).
These platforms also enable to run PR-Agent specific tools automatically when a new PR is opened, or on each push to a branch.
For online usage, you will need to setup either a [GitHub App](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-app) or a [GitHub Action](https://qodo-merge-docs.qodo.ai/installation/github/#run-as-a-github-action) (GitHub), a [GitLab webhook](https://qodo-merge-docs.qodo.ai/installation/gitlab/#run-a-gitlab-webhook-server) (GitLab), or a [BitBucket App](https://qodo-merge-docs.qodo.ai/installation/bitbucket/#run-using-codiumai-hosted-bitbucket-app) (BitBucket).
These platforms also enable to run Qodo Merge specific tools automatically when a new PR is opened, or on each push to a branch.

View File

@ -1,15 +1,15 @@
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:
If you are subscribed to notifications for a repo with Qodo Merge, we recommend turning off notifications for PR comments, to avoid lengthy emails:
![notifications](https://codium.ai/images/pr_agent/notifications.png){width=512}
As an alternative, you can filter in your mail provider the notifications specifically from the PR-Agent bot, [see how](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).
As an alternative, you can filter in your mail provider the notifications specifically from the Qodo Merge bot, [see how](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).
![filter_mail_notifications](https://codium.ai/images/pr_agent/filter_mail_notifications.png){width=512}
Another option to reduce the mail overload, yet still receive notifications on PR-Agent tools, is to disable the help collapsible section in PR-Agent bot comments.
Another option to reduce the mail overload, yet still receive notifications on Qodo Merge tools, is to disable the help collapsible section in Qodo Merge bot comments.
This can done by setting `enable_help_text=false` for the relevant tool in the configuration file.
For example, to disable the help text for the `pr_reviewer` tool, set:
```

View File

@ -1,11 +1,11 @@
site_name: PR-Agent Documentation
site_name: Qodo Merge (formerly known as PR-Agent)
repo_url: https://github.com/Codium-ai/pr-agent
repo_name: Codium-ai/pr-agent
nav:
- Overview:
- 'index.md'
- 💎 PR-Agent Pro: 'overview/pr_agent_pro.md'
- 💎 Qodo Merge Pro: 'overview/pr_agent_pro.md'
- Data Privacy: 'overview/data_privacy.md'
- Installation:
- 'installation/index.md'
@ -14,7 +14,7 @@ nav:
- GitLab: 'installation/gitlab.md'
- BitBucket: 'installation/bitbucket.md'
- Azure DevOps: 'installation/azure.md'
- 💎 PR-Agent Pro: 'installation/pr_agent_pro.md'
- 💎 Qodo Merge Pro: 'installation/pr_agent_pro.md'
- Usage Guide:
- 'usage-guide/index.md'
- Introduction: 'usage-guide/introduction.md'
@ -23,7 +23,7 @@ nav:
- Managing Mail Notifications: 'usage-guide/mail_notifications.md'
- Changing a Model: 'usage-guide/changing_a_model.md'
- Additional Configurations: 'usage-guide/additional_configurations.md'
- 💎 PR-Agent Pro Models: 'usage-guide/PR_agent_pro_models'
- 💎 Qodo Merge Pro Models: 'usage-guide/PR_agent_pro_models'
- Tools:
- 'tools/index.md'
- Describe: 'tools/describe.md'
@ -41,12 +41,24 @@ nav:
- 💎 Custom Prompt: 'tools/custom_prompt.md'
- 💎 CI Feedback: 'tools/ci_feedback.md'
- 💎 Similar Code: 'tools/similar_code.md'
- Core Abilities: 'core-abilities/index.md'
- Core Abilities:
- 'core-abilities/index.md'
- Local and global metadata: 'core-abilities/metadata.md'
- Dynamic context: 'core-abilities/dynamic_context.md'
- Self-reflection: 'core-abilities/self_reflection.md'
- Impact evaluation: 'core-abilities/impact_evaluation.md'
- Interactivity: 'core-abilities/interactivity.md'
- Compression strategy: 'core-abilities/compression_strategy.md'
- Code-oriented YAML: 'core-abilities/code_oriented_yaml.md'
- Static code analysis: 'core-abilities/static_code_analysis.md'
- Code Fine-tuning Benchmark: 'finetuning_benchmark/index.md'
- Chrome Extension:
- PR-Agent Chrome Extension: 'chrome-extension/index.md'
- Qodo Merge Chrome Extension: 'chrome-extension/index.md'
- Features: 'chrome-extension/features.md'
- Data Privacy: 'chrome-extension/data_privacy.md'
- Code Fine-tuning Benchmark: 'finetuning_benchmark/index.md'
- FAQ:
- FAQ: 'faq/index.md'
# - Code Fine-tuning Benchmark: 'finetuning_benchmark/index.md'
theme:
logo: assets/logo.svg

View File

@ -19,6 +19,10 @@ MAX_TOKENS = {
'gpt-4o-mini': 128000, # 128K, but may be limited by config.max_model_tokens
'gpt-4o-mini-2024-07-18': 128000, # 128K, but may be limited by config.max_model_tokens
'gpt-4o-2024-08-06': 128000, # 128K, but may be limited by config.max_model_tokens
'o1-mini': 128000, # 128K, but may be limited by config.max_model_tokens
'o1-mini-2024-09-12': 128000, # 128K, but may be limited by config.max_model_tokens
'o1-preview': 128000, # 128K, but may be limited by config.max_model_tokens
'o1-preview-2024-09-12': 128000, # 128K, but may be limited by config.max_model_tokens
'claude-instant-1': 100000,
'claude-2': 100000,
'command-nightly': 4096,

View File

@ -167,7 +167,7 @@ class LiteLLMAIHandler(BaseAiHandler):
if self.azure:
model = 'azure/' + model
if 'claude' in model and not system:
system = "\n"
system = "No system prompt provided"
get_logger().warning(
"Empty system prompt for claude model. Adding a newline character to prevent OpenAI API error.")
messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
@ -215,13 +215,13 @@ class LiteLLMAIHandler(BaseAiHandler):
response = await acompletion(**kwargs)
except (openai.APIError, openai.APITimeoutError) as e:
get_logger().warning("Error during OpenAI inference: ", e)
get_logger().warning(f"Error during LLM inference: {e}")
raise
except (openai.RateLimitError) as e:
get_logger().error("Rate limit error during OpenAI inference: ", e)
get_logger().error(f"Rate limit error during LLM inference: {e}")
raise
except (Exception) as e:
get_logger().warning("Unknown error during OpenAI inference: ", e)
get_logger().warning(f"Unknown error during LLM inference: {e}")
raise openai.APIError from e
if response is None or len(response["choices"]) == 0:
raise openai.APIError

View File

@ -1,6 +1,7 @@
from __future__ import annotations
import re
import traceback
from pr_agent.config_loader import get_settings
from pr_agent.algo.types import EDIT_TYPE, FilePatchInfo
@ -12,27 +13,48 @@ def extend_patch(original_file_str, patch_str, patch_extra_lines_before=0,
if not patch_str or (patch_extra_lines_before == 0 and patch_extra_lines_after == 0) or not original_file_str:
return patch_str
if type(original_file_str) == bytes:
original_file_str = decode_if_bytes(original_file_str)
if not original_file_str:
return patch_str
if should_skip_patch(filename):
return patch_str
try:
extended_patch_str = process_patch_lines(patch_str, original_file_str,
patch_extra_lines_before, patch_extra_lines_after)
except Exception as e:
get_logger().warning(f"Failed to extend patch: {e}", artifact={"traceback": traceback.format_exc()})
return patch_str
return extended_patch_str
def decode_if_bytes(original_file_str):
if isinstance(original_file_str, bytes):
try:
original_file_str = original_file_str.decode('utf-8')
return original_file_str.decode('utf-8')
except UnicodeDecodeError:
encodings_to_try = ['iso-8859-1', 'latin-1', 'ascii', 'utf-16']
for encoding in encodings_to_try:
try:
return original_file_str.decode(encoding)
except UnicodeDecodeError:
continue
return ""
return original_file_str
# skip patches
patch_extension_skip_types = get_settings().config.patch_extension_skip_types #[".md",".txt"]
def should_skip_patch(filename):
patch_extension_skip_types = get_settings().config.patch_extension_skip_types
if patch_extension_skip_types and filename:
if any([filename.endswith(skip_type) for skip_type in patch_extension_skip_types]):
return patch_str
return any(filename.endswith(skip_type) for skip_type in patch_extension_skip_types)
return False
# dynamic context settings
def process_patch_lines(patch_str, original_file_str, patch_extra_lines_before, patch_extra_lines_after):
allow_dynamic_context = get_settings().config.allow_dynamic_context
max_extra_lines_before_dynamic_context = get_settings().config.max_extra_lines_before_dynamic_context
patch_extra_lines_before_dynamic = patch_extra_lines_before
if allow_dynamic_context:
if max_extra_lines_before_dynamic_context > patch_extra_lines_before:
patch_extra_lines_before_dynamic = max_extra_lines_before_dynamic_context
else:
get_logger().warning(f"'max_extra_lines_before_dynamic_context' should be greater than 'patch_extra_lines_before'")
patch_extra_lines_before_dynamic = get_settings().config.max_extra_lines_before_dynamic_context
original_lines = original_file_str.splitlines()
len_original_lines = len(original_lines)
@ -46,23 +68,14 @@ def extend_patch(original_file_str, patch_str, patch_extra_lines_before=0,
for line in patch_lines:
if line.startswith('@@'):
match = RE_HUNK_HEADER.match(line)
# identify hunk header
if match:
# finish last hunk
# finish processing previous hunk
if start1 != -1 and patch_extra_lines_after > 0:
delta_lines = original_lines[start1 + size1 - 1:start1 + size1 - 1 + patch_extra_lines_after]
delta_lines = [f' {line}' for line in delta_lines]
delta_lines = [f' {line}' for line in original_lines[start1 + size1 - 1:start1 + size1 - 1 + patch_extra_lines_after]]
extended_patch_lines.extend(delta_lines)
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]
section_header, size1, size2, start1, start2 = extract_hunk_headers(match)
if patch_extra_lines_before > 0 or patch_extra_lines_after > 0:
def _calc_context_limits(patch_lines_before):
@ -82,29 +95,30 @@ def extend_patch(original_file_str, patch_str, patch_extra_lines_before=0,
_calc_context_limits(patch_extra_lines_before_dynamic)
lines_before = original_lines[extended_start1 - 1:start1 - 1]
found_header = False
for i,line, in enumerate(lines_before):
for i, line, in enumerate(lines_before):
if section_header in line:
found_header = True
# Update start and size in one line each
extended_start1, extended_start2 = extended_start1 + i, extended_start2 + i
extended_size1, extended_size2 = extended_size1 - i, extended_size2 - i
get_logger().debug(f"Found section header in line {i} before the hunk")
# get_logger().debug(f"Found section header in line {i} before the hunk")
section_header = ''
break
if not found_header:
get_logger().debug(f"Section header not found in the extra lines before the hunk")
# get_logger().debug(f"Section header not found in the extra lines before the hunk")
extended_start1, extended_size1, extended_start2, extended_size2 = \
_calc_context_limits(patch_extra_lines_before)
else:
extended_start1, extended_size1, extended_start2, extended_size2 = \
_calc_context_limits(patch_extra_lines_before)
delta_lines = original_lines[extended_start1 - 1:start1 - 1]
delta_lines = [f' {line}' for line in delta_lines]
delta_lines = [f' {line}' for line in original_lines[extended_start1 - 1:start1 - 1]]
# logic to remove section header if its in the extra delta lines (in dynamic context, this is also done)
if section_header and not allow_dynamic_context:
for line in delta_lines:
if section_header in line:
section_header = '' # remove section header if it is in the extra delta lines
section_header = '' # remove section header if it is in the extra delta lines
break
else:
extended_start1 = start1
@ -120,11 +134,10 @@ def extend_patch(original_file_str, patch_str, patch_extra_lines_before=0,
continue
extended_patch_lines.append(line)
except Exception as e:
if get_settings().config.verbosity_level >= 2:
get_logger().error(f"Failed to extend patch: {e}")
get_logger().warning(f"Failed to extend patch: {e}", artifact={"traceback": traceback.format_exc()})
return patch_str
# finish last hunk
# finish processing last hunk
if start1 != -1 and patch_extra_lines_after > 0:
delta_lines = original_lines[start1 + size1 - 1:start1 + size1 - 1 + patch_extra_lines_after]
# add space at the beginning of each extra line
@ -135,6 +148,20 @@ def extend_patch(original_file_str, patch_str, patch_extra_lines_before=0,
return extended_patch_str
def extract_hunk_headers(match):
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]
return section_header, size1, size2, start1, start2
def omit_deletion_hunks(patch_lines) -> str:
"""
Omit deletion hunks from the patch and return the modified patch.
@ -243,7 +270,7 @@ __old hunk__
if hasattr(file, 'edit_type') and file.edit_type == EDIT_TYPE.DELETED:
return f"\n\n## file '{file.filename.strip()}' was deleted\n"
patch_with_lines_str = f"\n\n## file: '{file.filename.strip()}'\n"
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+))? @@[ ]?(.*)")
@ -253,8 +280,8 @@ __old hunk__
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():
for line_i, line in enumerate(patch_lines):
if 'no newline at end of file' in line.lower().strip().strip('//'):
continue
if line.startswith('@@'):
@ -280,21 +307,18 @@ __old hunk__
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
section_header, size1, size2, start1, start2 = extract_hunk_headers(match)
elif line.startswith('+'):
new_content_lines.append(line)
elif line.startswith('-'):
old_content_lines.append(line)
else:
if not line and line_i: # if this line is empty and the next line is a hunk header, skip it
if line_i + 1 < len(patch_lines) and patch_lines[line_i + 1].startswith('@@'):
continue
elif line_i + 1 == len(patch_lines):
continue
new_content_lines.append(line)
old_content_lines.append(line)
@ -319,7 +343,7 @@ __old hunk__
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"
patch_with_lines_str = f"\n\n## File: '{file_name.strip()}'\n\n"
selected_lines = ""
patch_lines = patch.splitlines()
RE_HUNK_HEADER = re.compile(
@ -339,15 +363,7 @@ def extract_hunk_lines_from_patch(patch: str, file_name, line_start, line_end, s
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
section_header, size1, size2, start1, start2 = extract_hunk_headers(match)
# check if line range is in this hunk
if side.lower() == 'left':

View File

@ -23,8 +23,15 @@ ADDED_FILES_ = "Additional added files (insufficient token budget to process):\n
OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD = 1500
OUTPUT_BUFFER_TOKENS_HARD_THRESHOLD = 1000
MAX_EXTRA_LINES = 10
def cap_and_log_extra_lines(value, direction) -> int:
if value > MAX_EXTRA_LINES:
get_logger().warning(f"patch_extra_lines_{direction} was {value}, capping to {MAX_EXTRA_LINES}")
return MAX_EXTRA_LINES
return value
def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler,
model: str,
@ -38,6 +45,8 @@ def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler,
else:
PATCH_EXTRA_LINES_BEFORE = get_settings().config.patch_extra_lines_before
PATCH_EXTRA_LINES_AFTER = get_settings().config.patch_extra_lines_after
PATCH_EXTRA_LINES_BEFORE = cap_and_log_extra_lines(PATCH_EXTRA_LINES_BEFORE, "before")
PATCH_EXTRA_LINES_AFTER = cap_and_log_extra_lines(PATCH_EXTRA_LINES_AFTER, "after")
try:
diff_files_original = git_provider.get_diff_files()
@ -200,6 +209,10 @@ def pr_generate_extended_diff(pr_languages: list,
if add_line_numbers_to_hunks:
full_extended_patch = convert_to_hunks_with_lines_numbers(extended_patch, file)
# add AI-summary metadata to the patch
if file.ai_file_summary and get_settings().get("config.enable_ai_metadata", False):
full_extended_patch = add_ai_summary_top_patch(file, full_extended_patch)
patch_tokens = token_handler.count_tokens(full_extended_patch)
file.tokens = patch_tokens
total_tokens += patch_tokens
@ -239,6 +252,10 @@ def pr_generate_compressed_diff(top_langs: list, token_handler: TokenHandler, mo
if convert_hunks_to_line_numbers:
patch = convert_to_hunks_with_lines_numbers(patch, file)
## add AI-summary metadata to the patch (disabled, since we are in the compressed diff)
# if file.ai_file_summary and get_settings().config.get('config.is_auto_command', False):
# patch = add_ai_summary_top_patch(file, patch)
new_patch_tokens = token_handler.count_tokens(patch)
file_dict[file.filename] = {'patch': patch, 'tokens': new_patch_tokens, 'edit_type': file.edit_type}
@ -304,7 +321,7 @@ def generate_full_patch(convert_hunks_to_line_numbers, file_dict, max_tokens_mod
if patch:
if not convert_hunks_to_line_numbers:
patch_final = f"\n\n## file: '{filename.strip()}\n\n{patch.strip()}\n'"
patch_final = f"\n\n## File: '{filename.strip()}\n\n{patch.strip()}\n'"
else:
patch_final = "\n\n" + patch.strip()
patches.append(patch_final)
@ -330,11 +347,9 @@ async def retry_with_fallback_models(f: Callable, model_type: ModelType = ModelT
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
raise Exception(f"Failed to generate prediction with any model of {all_models}")
def _get_all_models(model_type: ModelType = ModelType.REGULAR) -> List[str]:
@ -400,11 +415,17 @@ def get_pr_multi_diffs(git_provider: GitProvider,
for lang in pr_languages:
sorted_files.extend(sorted(lang['files'], key=lambda x: x.tokens, reverse=True))
# Get the maximum number of extra lines before and after the patch
PATCH_EXTRA_LINES_BEFORE = get_settings().config.patch_extra_lines_before
PATCH_EXTRA_LINES_AFTER = get_settings().config.patch_extra_lines_after
PATCH_EXTRA_LINES_BEFORE = cap_and_log_extra_lines(PATCH_EXTRA_LINES_BEFORE, "before")
PATCH_EXTRA_LINES_AFTER = cap_and_log_extra_lines(PATCH_EXTRA_LINES_AFTER, "after")
# 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,
patch_extra_lines_before=get_settings().config.patch_extra_lines_before,
patch_extra_lines_after=get_settings().config.patch_extra_lines_after)
patch_extra_lines_before=PATCH_EXTRA_LINES_BEFORE,
patch_extra_lines_after=PATCH_EXTRA_LINES_AFTER)
# if we are under the limit, return the full diff
if total_tokens + OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD < get_max_tokens(model):
@ -432,6 +453,9 @@ def get_pr_multi_diffs(git_provider: GitProvider,
continue
patch = convert_to_hunks_with_lines_numbers(patch, file)
# add AI-summary metadata to the patch
if file.ai_file_summary and get_settings().get("config.enable_ai_metadata", False):
patch = add_ai_summary_top_patch(file, patch)
new_patch_tokens = token_handler.count_tokens(patch)
if patch and (token_handler.prompt_tokens + new_patch_tokens) > get_max_tokens(
@ -479,3 +503,46 @@ def get_pr_multi_diffs(git_provider: GitProvider,
final_diff_list.append(final_diff)
return final_diff_list
def add_ai_metadata_to_diff_files(git_provider, pr_description_files):
"""
Adds AI metadata to the diff files based on the PR description files (FilePatchInfo.ai_file_summary).
"""
try:
if not pr_description_files:
get_logger().warning(f"PR description files are empty.")
return
available_files = {pr_file['full_file_name'].strip(): pr_file for pr_file in pr_description_files}
diff_files = git_provider.get_diff_files()
found_any_match = False
for file in diff_files:
filename = file.filename.strip()
if filename in available_files:
file.ai_file_summary = available_files[filename]
found_any_match = True
if not found_any_match:
get_logger().error(f"Failed to find any matching files between PR description and diff files.",
artifact={"pr_description_files": pr_description_files})
except Exception as e:
get_logger().error(f"Failed to add AI metadata to diff files: {e}",
artifact={"traceback": traceback.format_exc()})
def add_ai_summary_top_patch(file, full_extended_patch):
try:
# below every instance of '## File: ...' in the patch, add the ai-summary metadata
full_extended_patch_lines = full_extended_patch.split("\n")
for i, line in enumerate(full_extended_patch_lines):
if line.startswith("## File:") or line.startswith("## file:"):
full_extended_patch_lines.insert(i + 1,
f"### AI-generated changes summary:\n{file.ai_file_summary['long_summary']}")
full_extended_patch = "\n".join(full_extended_patch_lines)
return full_extended_patch
# if no '## File: ...' was found
return full_extended_patch
except Exception as e:
get_logger().error(f"Failed to add AI summary to the top of the patch: {e}",
artifact={"traceback": traceback.format_exc()})
return full_extended_patch

View File

@ -21,3 +21,4 @@ class FilePatchInfo:
old_filename: str = None
num_plus_lines: int = -1
num_minus_lines: int = -1
ai_file_summary: str = None

View File

@ -1,4 +1,5 @@
from __future__ import annotations
import html2text
import html
import copy
@ -99,8 +100,8 @@ def convert_to_markdown_v2(output_data: dict,
emojis = {
"Can be split": "🔀",
"Possible issues": "",
"Key issues to review": "",
"Recommended focus areas for review": "",
"Score": "🏅",
"Relevant tests": "🧪",
"Focused PR": "",
@ -119,6 +120,9 @@ def convert_to_markdown_v2(output_data: dict,
if not output_data or not output_data.get('review', {}):
return ""
if get_settings().get("pr_reviewer.enable_intro_text", False):
markdown_text += f"Here are some key observations to aid the review process:\n\n"
if gfm_supported:
markdown_text += "<table>\n"
@ -188,23 +192,21 @@ def convert_to_markdown_v2(output_data: dict,
if is_value_no(value):
if gfm_supported:
markdown_text += f"<tr><td>"
markdown_text += f"{emoji}&nbsp;<strong>No key issues to review</strong>"
markdown_text += f"{emoji}&nbsp;<strong>No major issues detected</strong>"
markdown_text += f"</td></tr>\n"
else:
markdown_text += f"### {emoji} No key issues to review\n\n"
markdown_text += f"### {emoji} No major issues detected\n\n"
else:
# issues = value.split('\n- ')
issues =value
# for i, _ in enumerate(issues):
# issues[i] = issues[i].strip().strip('-').strip()
issues = value
if gfm_supported:
markdown_text += f"<tr><td>"
markdown_text += f"{emoji}&nbsp;<strong>{key_nice}</strong><br><br>\n\n"
# markdown_text += f"{emoji}&nbsp;<strong>{key_nice}</strong><br><br>\n\n"
markdown_text += f"{emoji}&nbsp;<strong>Recommended focus areas for review</strong><br><br>\n\n"
else:
markdown_text += f"### {emoji} Key issues to review\n\n#### \n"
markdown_text += f"### {emoji} Recommended focus areas for review\n\n#### \n"
for i, issue in enumerate(issues):
try:
if not issue:
if not issue or not isinstance(issue, dict):
continue
relevant_file = issue.get('relevant_file', '').strip()
issue_header = issue.get('issue_header', '').strip()
@ -214,25 +216,12 @@ def convert_to_markdown_v2(output_data: dict,
reference_link = git_provider.get_line_link(relevant_file, start_line, end_line)
if gfm_supported:
if get_settings().pr_reviewer.extra_issue_links:
issue_content_linked =copy.deepcopy(issue_content)
referenced_variables_list = issue.get('referenced_variables', [])
for component in referenced_variables_list:
name = component['variable_name'].strip().strip('`')
ind = issue_content.find(name)
if ind != -1:
reference_link_component = git_provider.get_line_link(relevant_file, component['relevant_line'], component['relevant_line'])
issue_content_linked = issue_content_linked[:ind-1] + f"[`{name}`]({reference_link_component})" + issue_content_linked[ind+len(name)+1:]
else:
get_logger().info(f"Failed to find variable in issue content: {component['variable_name'].strip()}")
issue_content = issue_content_linked
issue_str = f"<a href='{reference_link}'><strong>{issue_header}</strong></a><br>{issue_content}"
else:
issue_str = f"[**{issue_header}**]({reference_link})\n\n{issue_content}\n\n"
markdown_text += f"{issue_str}\n\n"
except Exception as e:
get_logger().exception(f"Failed to process key issues to review: {e}")
get_logger().exception(f"Failed to process 'Recommended focus areas for review': {e}")
if gfm_supported:
markdown_text += f"</td></tr>\n"
else:
@ -917,21 +906,24 @@ def github_action_output(output_data: dict, key_name: str):
def show_relevant_configurations(relevant_section: str) -> str:
forbidden_keys = ['ai_disclaimer', 'ai_disclaimer_title', 'ANALYTICS_FOLDER', 'secret_provider',
skip_keys = ['ai_disclaimer', 'ai_disclaimer_title', 'ANALYTICS_FOLDER', 'secret_provider', "skip_keys",
'trial_prefix_message', 'no_eligible_message', 'identity_provider', 'ALLOWED_REPOS','APP_NAME']
extra_skip_keys = get_settings().config.get('config.skip_keys', [])
if extra_skip_keys:
skip_keys.extend(extra_skip_keys)
markdown_text = ""
markdown_text += "\n<hr>\n<details> <summary><strong>🛠️ Relevant configurations:</strong></summary> \n\n"
markdown_text +="<br>These are the relevant [configurations](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml) for this tool:\n\n"
markdown_text += f"**[config**]\n```yaml\n\n"
for key, value in get_settings().config.items():
if key in forbidden_keys:
if key in skip_keys:
continue
markdown_text += f"{key}: {value}\n"
markdown_text += "\n```\n"
markdown_text += f"\n**[{relevant_section}]**\n```yaml\n\n"
for key, value in get_settings().get(relevant_section, {}).items():
if key in forbidden_keys:
if key in skip_keys:
continue
markdown_text += f"{key}: {value}\n"
markdown_text += "\n```"
@ -945,3 +937,73 @@ def is_value_no(value):
if value_str == 'no' or value_str == 'none' or value_str == 'false':
return True
return False
def process_description(description_full: str) -> Tuple[str, List]:
if not description_full:
return "", []
split_str = "### **Changes walkthrough** 📝"
description_split = description_full.split(split_str)
base_description_str = description_split[0]
changes_walkthrough_str = ""
files = []
if len(description_split) > 1:
changes_walkthrough_str = description_split[1]
else:
get_logger().debug("No changes walkthrough found")
try:
if changes_walkthrough_str:
# get the end of the table
if '</table>\n\n___' in changes_walkthrough_str:
end = changes_walkthrough_str.index("</table>\n\n___")
elif '\n___' in changes_walkthrough_str:
end = changes_walkthrough_str.index("\n___")
else:
end = len(changes_walkthrough_str)
changes_walkthrough_str = changes_walkthrough_str[:end]
h = html2text.HTML2Text()
h.body_width = 0 # Disable line wrapping
# find all the files
pattern = r'<tr>\s*<td>\s*(<details>\s*<summary>(.*?)</summary>(.*?)</details>)\s*</td>'
files_found = re.findall(pattern, changes_walkthrough_str, re.DOTALL)
for file_data in files_found:
try:
if isinstance(file_data, tuple):
file_data = file_data[0]
pattern = r'<details>\s*<summary><strong>(.*?)</strong>\s*<dd><code>(.*?)</code>.*?</summary>\s*<hr>\s*(.*?)\s*<li>(.*?)</details>'
res = re.search(pattern, file_data, re.DOTALL)
if not res or res.lastindex != 4:
pattern_back = r'<details>\s*<summary><strong>(.*?)</strong><dd><code>(.*?)</code>.*?</summary>\s*<hr>\s*(.*?)\n\n\s*(.*?)</details>'
res = re.search(pattern_back, file_data, re.DOTALL)
if res and res.lastindex == 4:
short_filename = res.group(1).strip()
short_summary = res.group(2).strip()
long_filename = res.group(3).strip()
long_summary = res.group(4).strip()
long_summary = long_summary.replace('<br> *', '\n*').replace('<br>','').replace('\n','<br>')
long_summary = h.handle(long_summary).strip()
if long_summary.startswith('\\-'):
long_summary = "* " + long_summary[2:]
elif not long_summary.startswith('*'):
long_summary = f"* {long_summary}"
files.append({
'short_file_name': short_filename,
'full_file_name': long_filename,
'short_summary': short_summary,
'long_summary': long_summary
})
else:
get_logger().error(f"Failed to parse description", artifact={'description': file_data})
except Exception as e:
get_logger().exception(f"Failed to process description: {e}", artifact={'description': file_data})
except Exception as e:
get_logger().exception(f"Failed to process description: {e}")
return base_description_str, files

View File

@ -27,8 +27,9 @@ global_settings = Dynaconf(
"settings/pr_update_changelog_prompts.toml",
"settings/pr_custom_labels.toml",
"settings/pr_add_docs.toml",
"settings/custom_labels.toml",
"settings/pr_help_prompts.toml",
"settings_prod/.secrets.toml",
"settings/custom_labels.toml"
]]
)

View File

@ -316,7 +316,7 @@ class AzureDevopsProvider(GitProvider):
new_file_content_str = new_file_content_str.content
except Exception as error:
get_logger().error(f"Failed to retrieve new file content of {file} at version {version}. Error: {str(error)}")
get_logger().error(f"Failed to retrieve new file content of {file} at version {version}", error=error)
# get_logger().error(
# "Failed to retrieve new file content of %s at version %s. Error: %s",
# file,
@ -347,7 +347,7 @@ class AzureDevopsProvider(GitProvider):
)
original_file_content_str = original_file_content_str.content
except Exception as error:
get_logger().error(f"Failed to retrieve original file content of {file} at version {version}. Error: {str(error)}")
get_logger().error(f"Failed to retrieve original file content of {file} at version {version}", error=error)
original_file_content_str = ""
patch = load_large_diff(
@ -375,12 +375,12 @@ class AzureDevopsProvider(GitProvider):
self.diff_files = diff_files
return diff_files
except Exception as e:
print(f"Error: {str(e)}")
get_logger().exception(f"Failed to get diff files, error: {e}")
return []
def publish_comment(self, pr_comment: str, is_temporary: bool = False, thread_context=None):
comment = Comment(content=pr_comment)
thread = CommentThread(comments=[comment], thread_context=thread_context, status=1)
thread = CommentThread(comments=[comment], thread_context=thread_context, status=5)
thread_response = self.azure_devops_client.create_thread(
comment_thread=thread,
project=self.workspace_slug,
@ -516,16 +516,9 @@ class AzureDevopsProvider(GitProvider):
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):
threads = self.azure_devops_client.get_threads(repository_id=self.repo_slug, pull_request_id=self.pr_num, project=self.workspace_slug)
threads.reverse()
@ -549,18 +542,20 @@ class AzureDevopsProvider(GitProvider):
parsed_url = urlparse(pr_url)
path_parts = parsed_url.path.strip("/").split("/")
if len(path_parts) < 6 or path_parts[4] != "pullrequest":
if "pullrequest" not in path_parts:
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:
if len(path_parts) == 6: # "https://dev.azure.com/organization/project/_git/repo/pullrequest/1"
workspace_slug = path_parts[1]
repo_slug = path_parts[3]
pr_number = int(path_parts[5])
except ValueError as e:
raise ValueError("Unable to convert PR number to integer") from e
elif len(path_parts) == 5: # 'https://organization.visualstudio.com/project/_git/repo/pullrequest/1'
workspace_slug = path_parts[0]
repo_slug = path_parts[2]
pr_number = int(path_parts[4])
else:
raise ValueError("The provided URL does not appear to be a Azure DevOps PR URL")
return workspace_slug, repo_slug, pr_number
@ -620,3 +615,6 @@ class AzureDevopsProvider(GitProvider):
get_logger().error(f"Failed to get pr id, error: {e}")
return ""
def publish_file_comments(self, file_comments: list) -> bool:
pass

View File

@ -227,7 +227,10 @@ class BitbucketProvider(GitProvider):
try:
counter_valid += 1
if counter_valid < MAX_FILES_ALLOWED_FULL // 2: # factor 2 because bitbucket has limited API calls
if get_settings().get("bitbucket_app.avoid_full_files", False):
original_file_content_str = ""
new_file_content_str = ""
elif counter_valid < MAX_FILES_ALLOWED_FULL // 2: # factor 2 because bitbucket has limited API calls
if diff.old.get_data("links"):
original_file_content_str = self._get_pr_file_content(
diff.old.get_data("links")['self']['href'])

View File

@ -3,7 +3,7 @@ from abc import ABC, abstractmethod
# enum EDIT_TYPE (ADDED, DELETED, MODIFIED, RENAMED)
from typing import Optional
from pr_agent.algo.utils import Range
from pr_agent.algo.utils import Range, process_description
from pr_agent.config_loader import get_settings
from pr_agent.algo.types import FilePatchInfo
from pr_agent.log import get_logger
@ -61,14 +61,20 @@ class GitProvider(ABC):
def reply_to_comment_from_comment_id(self, comment_id: int, body: str):
pass
def get_pr_description(self, *, full: bool = True) -> str:
def get_pr_description(self, full: bool = True, split_changes_walkthrough=False) -> str or tuple:
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
if split_changes_walkthrough:
description, files = process_description(description)
if max_tokens_description:
description = clip_tokens(description, max_tokens_description)
return description, files
else:
if max_tokens_description:
description = 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):

View File

@ -551,7 +551,7 @@ class GitLabProvider(GitProvider):
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}"
link = f"{self.gl.url}/{self.id_project}/-/blob/{self.mr.source_branch}/{relevant_file}?ref_type=heads#L{relevant_line_start}-{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

View File

@ -68,6 +68,7 @@ def authorize(credentials: HTTPBasicCredentials = Depends(security)):
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}")
get_settings().set("config.is_auto_command", True)
for command in commands:
try:
split_command = command.split(" ")

View File

@ -3,6 +3,7 @@ import copy
import hashlib
import json
import os
import re
import time
import jwt
@ -77,6 +78,7 @@ async def handle_manifest(request: Request, response: Response):
async def _perform_commands_bitbucket(commands_conf: str, agent: PRAgent, api_url: str, log_context: dict):
apply_repo_settings(api_url)
commands = get_settings().get(f"bitbucket_app.{commands_conf}", {})
get_settings().set("config.is_auto_command", True)
for command in commands:
try:
split_command = command.split(" ")
@ -91,6 +93,48 @@ async def _perform_commands_bitbucket(commands_conf: str, agent: PRAgent, api_ur
get_logger().error(f"Failed to perform command {command}: {e}")
def is_bot_user(data) -> bool:
try:
if data["data"]["actor"]["type"] != "user":
get_logger().info(f"BitBucket actor type is not 'user': {data['data']['actor']['type']}")
return True
except Exception as e:
get_logger().error("Failed 'is_bot_user' logic: {e}")
return False
def should_process_pr_logic(data) -> bool:
try:
pr_data = data.get("data", {}).get("pullrequest", {})
title = pr_data.get("title", "")
source_branch = pr_data.get("source", {}).get("branch", {}).get("name", "")
target_branch = pr_data.get("destination", {}).get("branch", {}).get("name", "")
# logic to ignore PRs with specific titles
if title:
ignore_pr_title_re = get_settings().get("CONFIG.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 config.ignore_pr_title setting")
return False
ignore_pr_source_branches = get_settings().get("CONFIG.IGNORE_PR_SOURCE_BRANCHES", [])
ignore_pr_target_branches = get_settings().get("CONFIG.IGNORE_PR_TARGET_BRANCHES", [])
if (ignore_pr_source_branches or ignore_pr_target_branches):
if any(re.search(regex, source_branch) for regex in ignore_pr_source_branches):
get_logger().info(
f"Ignoring PR with source branch '{source_branch}' due to config.ignore_pr_source_branches settings")
return False
if any(re.search(regex, target_branch) for regex in ignore_pr_target_branches):
get_logger().info(
f"Ignoring PR with target branch '{target_branch}' due to config.ignore_pr_target_branches settings")
return False
except Exception as e:
get_logger().error(f"Failed 'should_process_pr_logic': {e}")
return True
@router.post("/webhook")
async def handle_github_webhooks(background_tasks: BackgroundTasks, request: Request):
app_name = get_settings().get("CONFIG.APP_NAME", "Unknown")
@ -101,13 +145,17 @@ async def handle_github_webhooks(background_tasks: BackgroundTasks, request: Req
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":
# ignore bot users
if is_bot_user(data):
return "OK"
# Check if the PR should be processed
if data.get("event", "") == "pullrequest:created":
if not should_process_pr_logic(data):
return "OK"
except KeyError:
get_logger().error("Failed to get actor type, check previous logs, this shouldn't happen.")
# Get the username of the sender
try:
@ -146,16 +194,6 @@ async def handle_github_webhooks(background_tasks: BackgroundTasks, request: Req
sender_id, pr_url) is not Eligibility.NOT_ELIGIBLE:
if get_settings().get("bitbucket_app.pr_commands"):
await _perform_commands_bitbucket("pr_commands", PRAgent(), pr_url, log_context)
else: # backwards compatibility
auto_review = get_setting_or_env("BITBUCKET_APP.AUTO_REVIEW", None)
if is_true(auto_review): # by default, auto review is disabled
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()
elif event == "pullrequest:comment_created":
pr_url = data["data"]["pullrequest"]["links"]["html"]["href"]
log_context["api_url"] = pr_url

View File

@ -80,10 +80,14 @@ async def run_action():
except Exception as e:
get_logger().info(f"github action: failed to apply repo settings: {e}")
# Handle pull request event
# Handle pull request opened event
if GITHUB_EVENT_NAME == "pull_request":
action = event_payload.get("action")
if action in ["opened", "reopened", "ready_for_review", "review_requested"]:
# Retrieve the list of actions from the configuration
pr_actions = get_settings().get("GITHUB_ACTION_CONFIG.PR_ACTIONS", ["opened", "reopened", "ready_for_review", "review_requested"])
if action in pr_actions:
pr_url = event_payload.get("pull_request", {}).get("url")
if pr_url:
# legacy - supporting both GITHUB_ACTION and GITHUB_ACTION_CONFIG
@ -97,9 +101,13 @@ async def run_action():
if auto_improve is None:
auto_improve = get_setting_or_env("GITHUB_ACTION_CONFIG.AUTO_IMPROVE", None)
# Set the configuration for auto actions
get_settings().config.is_auto_command = True # Set the flag to indicate that the command is auto
get_settings().pr_description.final_update_message = False # No final update message when auto_describe is enabled
get_logger().info(f"Running auto actions: auto_describe={auto_describe}, auto_review={auto_review}, auto_improve={auto_improve}")
# invoke by default all three tools
if auto_describe is None or is_true(auto_describe):
get_settings().pr_description.final_update_message = False # No final update message when auto_describe is enabled
await PRDescription(pr_url).run()
if auto_review is None or is_true(auto_review):
await PRReviewer(pr_url).run()

View File

@ -128,8 +128,6 @@ async def handle_new_pr_opened(body: Dict[str, Any],
log_context: Dict[str, Any],
agent: PRAgent):
title = body.get("pull_request", {}).get("title", "")
get_settings().config.is_auto_command = True
pull_request, api_url = _check_pull_request_event(action, body, log_context)
if not (pull_request and api_url):
@ -138,13 +136,6 @@ async def handle_new_pr_opened(body: Dict[str, Any],
if action in get_settings().github_app.handle_pr_actions: # ['opened', 'reopened', 'ready_for_review']
# logic to ignore PRs with specific titles (e.g. "[Auto] ...")
apply_repo_settings(api_url)
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 {}
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:
@ -246,6 +237,60 @@ def get_log_context(body, event, action, build_number):
return log_context, sender, sender_id, sender_type
def is_bot_user(sender, sender_type):
try:
# logic to ignore PRs opened by bot
if get_settings().get("GITHUB_APP.IGNORE_BOT_PR", False) and sender_type == "Bot":
if 'pr-agent' not in sender:
get_logger().info(f"Ignoring PR from '{sender=}' because it is a bot")
return True
except Exception as e:
get_logger().error(f"Failed 'is_bot_user' logic: {e}")
return False
def should_process_pr_logic(sender_type, sender, body) -> bool:
try:
pull_request = body.get("pull_request", {})
title = pull_request.get("title", "")
pr_labels = pull_request.get("labels", [])
source_branch = pull_request.get("head", {}).get("ref", "")
target_branch = pull_request.get("base", {}).get("ref", "")
# logic to ignore PRs with specific titles
if title:
ignore_pr_title_re = get_settings().get("CONFIG.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 config.ignore_pr_title setting")
return False
# logic to ignore PRs with specific labels or source branches or target branches.
ignore_pr_labels = get_settings().get("CONFIG.IGNORE_PR_LABELS", [])
if pr_labels and ignore_pr_labels:
labels = [label['name'] for label in pr_labels]
if any(label in ignore_pr_labels for label in labels):
labels_str = ", ".join(labels)
get_logger().info(f"Ignoring PR with labels '{labels_str}' due to config.ignore_pr_labels settings")
return False
ignore_pr_source_branches = get_settings().get("CONFIG.IGNORE_PR_SOURCE_BRANCHES", [])
ignore_pr_target_branches = get_settings().get("CONFIG.IGNORE_PR_TARGET_BRANCHES", [])
if pull_request and (ignore_pr_source_branches or ignore_pr_target_branches):
if any(re.search(regex, source_branch) for regex in ignore_pr_source_branches):
get_logger().info(
f"Ignoring PR with source branch '{source_branch}' due to config.ignore_pr_source_branches settings")
return False
if any(re.search(regex, target_branch) for regex in ignore_pr_target_branches):
get_logger().info(
f"Ignoring PR with target branch '{target_branch}' due to config.ignore_pr_target_branches settings")
return False
except Exception as e:
get_logger().error(f"Failed 'should_process_pr_logic': {e}")
return True
async def handle_request(body: Dict[str, Any], event: str):
"""
Handle incoming GitHub webhook requests.
@ -254,19 +299,20 @@ async def handle_request(body: Dict[str, Any], event: str):
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"
action = body.get("action") # "created", "opened", "reopened", "ready_for_review", "review_requested", "synchronize"
if not action:
return {}
agent = PRAgent()
log_context, sender, sender_id, sender_type = get_log_context(body, event, action, build_number)
# logic to ignore PRs opened by bot
if get_settings().get("GITHUB_APP.IGNORE_BOT_PR", False) and sender_type == "Bot":
if 'pr-agent' not in sender:
get_logger().info(f"Ignoring PR from '{sender=}' because it is a bot")
# logic to ignore PRs opened by bot, PRs with specific titles, labels, source branches, or target branches
if is_bot_user(sender, sender_type):
return {}
if action != 'created' and 'check_run' not in body:
if not should_process_pr_logic(sender_type, sender, body):
return {}
if 'check_run' in body: # handle failed checks
if 'check_run' in body: # handle failed checks
# get_logger().debug(f'Request body', artifact=body, event=event) # added inside handle_checks
pass
# handle comments on PRs
@ -281,7 +327,6 @@ async def handle_request(body: Dict[str, Any], event: str):
pass # handle_checkbox_clicked
# 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) # added inside handle_push_trigger_for_new_commits
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", ""):
@ -325,12 +370,14 @@ def _check_pull_request_event(action: str, body: dict, log_context: dict) -> Tup
return pull_request, api_url
async def _perform_auto_commands_github(commands_conf: str, agent: PRAgent, body: dict, api_url: str, log_context: dict):
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:
get_logger().info(f"New PR, but no auto commands configured")
return
get_settings().set("config.is_auto_command", True)
for command in commands:
split_command = command.split(" ")
command = split_command[0]

View File

@ -1,7 +1,10 @@
import asyncio
import multiprocessing
from collections import deque
import traceback
from datetime import datetime, timezone
import time
import requests
import aiohttp
from pr_agent.agent.pr_agent import PRAgent
@ -13,6 +16,15 @@ setup_logger(fmt=LoggingFormat.JSON, level="DEBUG")
NOTIFICATION_URL = "https://api.github.com/notifications"
async def mark_notification_as_read(headers, notification, session):
async with session.patch(
f"https://api.github.com/notifications/threads/{notification['id']}",
headers=headers) as mark_read_response:
if mark_read_response.status != 205:
get_logger().error(
f"Failed to mark notification as read. Status code: {mark_read_response.status}")
def now() -> str:
"""
Get the current UTC time in ISO 8601 format.
@ -24,6 +36,108 @@ def now() -> str:
now_utc = now_utc.replace("+00:00", "Z")
return now_utc
async def async_handle_request(pr_url, rest_of_comment, comment_id, git_provider):
agent = PRAgent()
success = await agent.handle_request(
pr_url,
rest_of_comment,
notify=lambda: git_provider.add_eyes_reaction(comment_id)
)
return success
def run_handle_request(pr_url, rest_of_comment, comment_id, git_provider):
return asyncio.run(async_handle_request(pr_url, rest_of_comment, comment_id, git_provider))
def process_comment_sync(pr_url, rest_of_comment, comment_id):
try:
# Run the async handle_request in a separate function
git_provider = get_git_provider()(pr_url=pr_url)
success = run_handle_request(pr_url, rest_of_comment, comment_id, git_provider)
except Exception as e:
get_logger().error(f"Error processing comment: {e}", artifact={"traceback": traceback.format_exc()})
async def process_comment(pr_url, rest_of_comment, comment_id):
try:
git_provider = get_git_provider()(pr_url=pr_url)
git_provider.set_pr(pr_url)
agent = PRAgent()
success = await agent.handle_request(
pr_url,
rest_of_comment,
notify=lambda: git_provider.add_eyes_reaction(comment_id)
)
get_logger().info(f"Finished processing comment for PR: {pr_url}")
except Exception as e:
get_logger().error(f"Error processing comment: {e}", artifact={"traceback": traceback.format_exc()})
async def is_valid_notification(notification, headers, handled_ids, session, user_id):
try:
if 'reason' in notification and notification['reason'] == 'mention':
if 'subject' in notification and notification['subject']['type'] == 'PullRequest':
pr_url = notification['subject']['url']
latest_comment = notification['subject']['latest_comment_url']
if not latest_comment or not isinstance(latest_comment, str):
get_logger().debug(f"no latest_comment")
return False, handled_ids
async with session.get(latest_comment, headers=headers) as comment_response:
check_prev_comments = False
if comment_response.status == 200:
comment = await comment_response.json()
if 'id' in comment:
if comment['id'] in handled_ids:
get_logger().debug(f"comment['id'] in handled_ids")
return False, handled_ids
else:
handled_ids.add(comment['id'])
if 'user' in comment and 'login' in comment['user']:
if comment['user']['login'] == user_id:
get_logger().debug(f"comment['user']['login'] == user_id")
check_prev_comments = True
comment_body = comment.get('body', '')
if not comment_body:
get_logger().debug(f"no comment_body")
check_prev_comments = True
else:
user_tag = "@" + user_id
if user_tag not in comment_body:
get_logger().debug(f"user_tag not in comment_body")
check_prev_comments = True
else:
get_logger().info(f"Polling, pr_url: {pr_url}",
artifact={"comment": comment_body})
if not check_prev_comments:
return True, handled_ids, comment, comment_body, pr_url, user_tag
else: # we could not find the user tag in the latest comment. Check previous comments
# get all comments in the PR
requests_url = f"{pr_url}/comments".replace("pulls", "issues")
comments_response = requests.get(requests_url, headers=headers)
comments = comments_response.json()[::-1]
max_comment_to_scan = 4
for comment in comments[:max_comment_to_scan]:
if 'user' in comment and 'login' in comment['user']:
if comment['user']['login'] == user_id:
continue
comment_body = comment.get('body', '')
if not comment_body:
continue
if user_tag in comment_body:
get_logger().info("found user tag in previous comments")
get_logger().info(f"Polling, pr_url: {pr_url}",
artifact={"comment": comment_body})
return True, handled_ids, comment, comment_body, pr_url, user_tag
get_logger().error(f"Failed to fetch comments for PR: {pr_url}")
return False, handled_ids
return False, handled_ids
except Exception as e:
get_logger().error(f"Error processing notification: {e}", artifact={"traceback": traceback.format_exc()})
return False, handled_ids
async def polling_loop():
"""
@ -34,7 +148,6 @@ async def polling_loop():
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)
get_settings().set("pr_description.publish_description_as_comment", True)
@ -74,43 +187,45 @@ async def polling_loop():
notifications = await response.json()
if not notifications:
continue
get_logger().info(f"Received {len(notifications)} notifications")
task_queue = deque()
for notification in notifications:
if not notification:
continue
# mark notification as read
await mark_notification_as_read(headers, notification, session)
handled_ids.add(notification['id'])
if 'reason' in notification and notification['reason'] == 'mention':
if 'subject' in notification and notification['subject']['type'] == 'PullRequest':
pr_url = notification['subject']['url']
latest_comment = notification['subject']['latest_comment_url']
if not latest_comment or not isinstance(latest_comment, str):
continue
async with session.get(latest_comment, headers=headers) as comment_response:
if comment_response.status == 200:
comment = await comment_response.json()
if 'id' in comment:
if comment['id'] in handled_ids:
continue
else:
handled_ids.add(comment['id'])
if 'user' in comment and 'login' in comment['user']:
if comment['user']['login'] == user_id:
continue
comment_body = comment.get('body', '')
if not comment_body:
continue
commenter_github_user = comment['user']['login'] \
if 'user' in comment else ''
get_logger().info(f"Polling, pr_url: {pr_url}",
artifact={"comment": comment_body})
user_tag = "@" + user_id
if user_tag not in comment_body:
continue
rest_of_comment = comment_body.split(user_tag)[1].strip()
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)
output = await is_valid_notification(notification, headers, handled_ids, session, user_id)
if output[0]:
_, handled_ids, comment, comment_body, pr_url, user_tag = output
rest_of_comment = comment_body.split(user_tag)[1].strip()
comment_id = comment['id']
# Add to the task queue
get_logger().info(
f"Adding comment processing to task queue for PR, {pr_url}, comment_body: {comment_body}")
task_queue.append((process_comment_sync, (pr_url, rest_of_comment, comment_id)))
get_logger().info(f"Queued comment processing for PR: {pr_url}")
else:
get_logger().debug(f"Skipping comment processing for PR")
max_allowed_parallel_tasks = 10
if task_queue:
processes = []
for i, (func, args) in enumerate(task_queue): # Create parallel tasks
p = multiprocessing.Process(target=func, args=args)
processes.append(p)
p.start()
if i > max_allowed_parallel_tasks:
get_logger().error(
f"Dropping {len(task_queue) - max_allowed_parallel_tasks} tasks from polling session")
break
task_queue.clear()
# Dont wait for all processes to complete. Move on to the next iteration
# for p in processes:
# p.join()
elif response.status != 304:
print(f"Failed to fetch notifications. Status code: {response.status}")
@ -121,4 +236,4 @@ async def polling_loop():
if __name__ == '__main__':
asyncio.run(polling_loop())
asyncio.run(polling_loop())

View File

@ -1,4 +1,5 @@
import copy
import re
import json
from datetime import datetime
@ -61,6 +62,7 @@ async def _perform_commands_gitlab(commands_conf: str, agent: PRAgent, api_url:
log_context: dict):
apply_repo_settings(api_url)
commands = get_settings().get(f"gitlab.{commands_conf}", {})
get_settings().set("config.is_auto_command", True)
for command in commands:
try:
split_command = command.split(" ")
@ -75,6 +77,57 @@ async def _perform_commands_gitlab(commands_conf: str, agent: PRAgent, api_url:
get_logger().error(f"Failed to perform command {command}: {e}")
def is_bot_user(data) -> bool:
try:
# logic to ignore bot users (unlike Github, no direct flag for bot users in gitlab)
sender_name = data.get("user", {}).get("name", "unknown").lower()
bot_indicators = ['codium', 'bot_', 'bot-', '_bot', '-bot']
if any(indicator in sender_name for indicator in bot_indicators):
get_logger().info(f"Skipping GitLab bot user: {sender_name}")
return True
except Exception as e:
get_logger().error(f"Failed 'is_bot_user' logic: {e}")
return False
def should_process_pr_logic(data, title) -> bool:
try:
# logic to ignore MRs for titles, labels and source, target branches.
ignore_mr_title = get_settings().get("CONFIG.IGNORE_PR_TITLE", [])
ignore_mr_labels = get_settings().get("CONFIG.IGNORE_PR_LABELS", [])
ignore_mr_source_branches = get_settings().get("CONFIG.IGNORE_PR_SOURCE_BRANCHES", [])
ignore_mr_target_branches = get_settings().get("CONFIG.IGNORE_PR_TARGET_BRANCHES", [])
#
if ignore_mr_source_branches:
source_branch = data['object_attributes'].get('source_branch')
if any(re.search(regex, source_branch) for regex in ignore_mr_source_branches):
get_logger().info(
f"Ignoring MR with source branch '{source_branch}' due to gitlab.ignore_mr_source_branches settings")
return False
if ignore_mr_target_branches:
target_branch = data['object_attributes'].get('target_branch')
if any(re.search(regex, target_branch) for regex in ignore_mr_target_branches):
get_logger().info(
f"Ignoring MR with target branch '{target_branch}' due to gitlab.ignore_mr_target_branches settings")
return False
if ignore_mr_labels:
labels = [label['title'] for label in data['object_attributes'].get('labels', [])]
if any(label in ignore_mr_labels for label in labels):
labels_str = ", ".join(labels)
get_logger().info(f"Ignoring MR with labels '{labels_str}' due to gitlab.ignore_mr_labels settings")
return False
if ignore_mr_title:
if any(re.search(regex, title) for regex in ignore_mr_title):
get_logger().info(f"Ignoring MR with title '{title}' due to gitlab.ignore_mr_title settings")
return False
except Exception as e:
get_logger().error(f"Failed 'should_process_pr_logic': {e}")
return True
@router.post("/webhook")
async def gitlab_webhook(background_tasks: BackgroundTasks, request: Request):
@ -117,18 +170,20 @@ async def gitlab_webhook(background_tasks: BackgroundTasks, request: Request):
sender = data.get("user", {}).get("username", "unknown")
sender_id = data.get("user", {}).get("id", "unknown")
# logic to ignore bot users (unlike Github, no direct flag for bot users in gitlab)
sender_name = data.get("user", {}).get("name", "unknown").lower()
if 'codium' in sender_name or 'bot_' in sender_name or 'bot-' in sender_name or '_bot' in sender_name or '-bot' in sender_name:
get_logger().info(f"Skipping bot user: {sender_name}")
# ignore bot users
if is_bot_user(data):
return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder({"message": "success"}))
if data.get('event_type') != 'note' and data.get('object_attributes', {}): # not a comment
# ignore MRs based on title, labels, source and target branches
if not should_process_pr_logic(data, data['object_attributes'].get('title')):
return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder({"message": "success"}))
log_context["sender"] = sender
if data.get('object_kind') == 'merge_request' and data['object_attributes'].get('action') in ['open', 'reopen']:
title = data['object_attributes'].get('title')
url = data['object_attributes'].get('url')
draft = data['object_attributes'].get('draft')
get_logger().info(f"New merge request: {url}")
if draft:
get_logger().info(f"Skipping draft MR: {url}")
return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder({"message": "success"}))
@ -138,7 +193,7 @@ async def gitlab_webhook(background_tasks: BackgroundTasks, request: Request):
if 'merge_request' in data:
mr = data['merge_request']
url = mr.get('url')
get_logger().info(f"A comment has been added to a merge request: {url}")
body = data.get('object_attributes', {}).get('note')
if data.get('object_attributes', {}).get('type') == 'DiffNote' and '/ask' in body: # /ask_line

View File

@ -14,6 +14,7 @@ use_wiki_settings_file=true
use_repo_settings_file=true
use_global_settings_file=true
ai_timeout=120 # 2minutes
skip_keys = []
# token limits
max_description_tokens = 500
max_commits_tokens = 500
@ -21,8 +22,8 @@ max_model_tokens = 32000 # Limits the maximum number of tokens that can be used
custom_model_max_tokens=-1 # for models not in the default list
# patch extension logic
patch_extension_skip_types =[".md",".txt"]
allow_dynamic_context=false
max_extra_lines_before_dynamic_context = 10 # will try to include up to 10 extra lines before the hunk in the patch, until we reach an enclosing function or class
allow_dynamic_context=true
max_extra_lines_before_dynamic_context = 8 # will try to include up to 10 extra lines before the hunk in the patch, until we reach an enclosing function or class
patch_extra_lines_before = 3 # Number of extra lines (+3 default ones) to include before each hunk in the patch
patch_extra_lines_after = 1 # Number of extra lines (+3 default ones) to include after each hunk in the patch
secret_provider=""
@ -31,10 +32,17 @@ ai_disclaimer_title="" # Pro feature, title for a collapsible disclaimer to AI
ai_disclaimer="" # Pro feature, full text for the AI disclaimer
output_relevant_configurations=false
large_patch_policy = "clip" # "clip", "skip"
is_auto_command=false
# seed
seed=-1 # set positive value to fix the seed (and ensure temperature=0)
temperature=0.2
# ignore logic
ignore_pr_title = ["^\\[Auto\\]", "^Auto"] # a list of regular expressions to match against the PR title to ignore the PR agent
ignore_pr_target_branches = [] # a list of regular expressions of target branches to ignore from PR agent when an PR is created
ignore_pr_source_branches = [] # a list of regular expressions of source branches to ignore from PR agent when an PR is created
ignore_pr_labels = [] # labels to ignore from PR agent when an PR is created
#
is_auto_command = false # will be auto-set to true if the command is triggered by an automation
enable_ai_metadata = false # will enable adding ai metadata
[pr_reviewer] # /review #
# enable/disable features
@ -43,7 +51,6 @@ require_tests_review=true
require_estimate_effort_to_review=true
require_can_be_split_review=false
require_security_review=true
extra_issue_links=false
# 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.) ?"
@ -62,6 +69,7 @@ enable_review_labels_effort=true
require_all_thresholds_for_incremental_review=false
minimal_commits_for_incremental_review=0
minimal_minutes_for_incremental_review=0
enable_intro_text=true
enable_help_text=false # Determines whether to include help text in the PR review. Enabled by default.
# auto approval
enable_auto_approval=false
@ -101,18 +109,22 @@ enable_help_text=false
[pr_code_suggestions] # /improve #
max_context_tokens=14000
num_code_suggestions=4
#
commitable_code_suggestions = false
dual_publishing_score_threshold=-1 # -1 to disable, [0-10] to set the threshold (>=) for publishing a code suggestion both in a table and as commitable
#
extra_instructions = ""
rank_suggestions = false
enable_help_text=false
enable_chat_text=false
enable_intro_text=true
persistent_comment=true
max_history_len=4
# enable to apply suggestion 💎
apply_suggestions_checkbox=true
# suggestions scoring
self_reflect_on_suggestions=true
suggestions_score_threshold=0 # [0-10]. highly recommend not to set this value above 8, since above it may clip highly relevant suggestions
suggestions_score_threshold=0 # [0-10]| recommend not to set this value above 8, since above it may clip highly relevant suggestions
# params for '/improve --extended' mode
auto_extended_mode=true
num_code_suggestions_per_chunk=4
@ -176,6 +188,8 @@ enable_help_text=true
final_update_message = false
[pr_help] # /help #
force_local_db=false
num_retrieved_snippets=5
[pr_config] # /config #
@ -187,11 +201,13 @@ base_url = "https://api.github.com"
publish_inline_comments_fallback_with_verification = true
try_fix_invalid_inline_comments = true
app_name = "pr-agent"
ignore_bot_pr = 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
# pr_actions = ['opened', 'reopened', 'ready_for_review', 'review_requested']
[github_app]
# these toggles allows running the github app from custom deployments
@ -215,8 +231,6 @@ push_commands = [
"/describe",
"/review --pr_reviewer.num_code_suggestions=0",
]
ignore_pr_title = []
ignore_bot_pr = true
[gitlab]
url = "https://gitlab.com"
@ -237,7 +251,7 @@ pr_commands = [
"/review --pr_reviewer.num_code_suggestions=0",
"/improve --pr_code_suggestions.commitable_code_suggestions=true --pr_code_suggestions.suggestions_score_threshold=7",
]
avoid_full_files = false
[local]
# LocalGitProvider settings - uncomment to use paths other than default
@ -293,7 +307,7 @@ number_of_results = 5
[lancedb]
uri = "./lancedb"
[best_practices]
content = ""
max_lines_allowed = 800
enable_global_best_practices = false

View File

@ -63,6 +63,7 @@ extra = [
]
[language_extension_map_org]
"1C Enterprise" = ["*.bsl", ]
ABAP = [".abap", ]
"AGS Script" = [".ash", ]
AMPL = [".ampl", ]

View File

@ -5,7 +5,7 @@ Your task is to generate {{ docs_for_language }} for code components in the PR D
Example for the PR Diff format:
======
## file: 'src/file1.py'
## File: 'src/file1.py'
@@ -12,3 +12,4 @@ def func1():
__new hunk__
@ -25,7 +25,7 @@ __old hunk__
...
## file: 'src/file2.py'
## File: 'src/file2.py'
...
======

View File

@ -1,11 +1,16 @@
[pr_code_suggestions_prompt]
system="""You are PR-Reviewer, a language model that specializes in suggesting improvements to a Pull Request (PR) code.
Your task is to provide meaningful and actionable code suggestions, to improve the new code presented in a PR code diff (lines starting with '+').
system="""You are PR-Reviewer, an AI specializing in Pull Request (PR) code analysis and suggestions.
Your task is to examine the provided code diff, focusing on new code (lines prefixed with '+'), and offer concise, actionable suggestions to fix possible bugs and problems, and enhance code quality, readability, and performance.
The format we will use to present the PR code diff:
The PR code diff will be in the following structured format:
======
## file: 'src/file1.py'
## File: 'src/file1.py'
{%- if is_ai_metadata %}
### AI-generated changes summary:
* ...
* ...
{%- endif %}
@@ ... @@ def func1():
__new hunk__
@ -26,30 +31,31 @@ __old hunk__
...
## file: 'src/file2.py'
## File: 'src/file2.py'
...
======
- In this format, we separate each hunk of diff code to '__new hunk__' and '__old hunk__' sections. The '__new hunk__' section contains the new code of the chunk, and the '__old hunk__' section contains the old code, that was removed. If no new code was added in a specific hunk, '__new hunk__' section will not be presented. If no code was removed, '__old hunk__' section will not be presented.
- We also added line numbers for the '__new hunk__' code, to help you refer to the code lines in your suggestions. These line numbers are not part of the actual code, and should only used for reference.
- Code lines are prefixed with symbols ('+', '-', ' '). The '+' symbol indicates new code added in the PR, the '-' symbol indicates code removed in the PR, and the ' ' symbol indicates unchanged code. \
- In the format above, the diff is organized into separate '__new hunk__' and '__old hunk__' sections for each code chunk. '__new hunk__' contains the updated code, while '__old hunk__' shows the removed code. If no code was added or removed in a specific chunk, the corresponding section will be omitted.
- Line numbers were added for the '__new hunk__' sections to help referencing specific lines in the code suggestions. These numbers are for reference only and are not part of the actual code.
- Code lines are prefixed with symbols: '+' for new code added in the PR, '-' for code removed, and ' ' for unchanged code.
{%- if is_ai_metadata %}
- When available, an AI-generated summary will precede each file's diff, with a high-level overview of the changes. Note that this summary may not be fully accurate or complete.
{%- endif %}
Specific instructions for generating code suggestions:
- Provide up to {{ num_code_suggestions }} code suggestions.
- The suggestions should be diverse and insightful. They should focus on improving only the new code introduced in the PR, meaning lines from '__new hunk__' sections, starting with '+' (after the line numbers).
- Prioritize suggestions that address possible issues, major problems, and bugs in the PR code. Don't repeat changes already present in the PR. If there are no relevant suggestions for the PR, return an empty list.
- 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. Use the line numbers from the '__new hunk__' sections.
- Every time you cite variables or names from the code, use backticks ('`'). For example: 'ensure that `variable_name` is ...'
- Take into account that you are reviewing a PR code diff, and that the entire codebase is not available for you as context. Hence, avoid suggestions that might conflict with unseen parts of the codebase.
Specific guidelines for generating code suggestions:
- Provide up to {{ num_code_suggestions }} distinct and insightful code suggestions.
- Focus solely on enhancing new code introduced in the PR, identified by '+' prefixes in '__new hunk__' sections (after the line numbers).
- Prioritize suggestions that address potential issues, critical problems, and bugs in the PR code. Avoid repeating changes already implemented in the PR. If no pertinent suggestions are applicable, return an empty list.
- Avoid proposing additions of docstrings, type hints, or comments, or the removal of unused imports.
- When referencing variables or names from the code, enclose them in backticks (`). Example: "ensure that `variable_name` is..."
- Be mindful you are viewing a partial PR code diff, not the full codebase. Avoid suggestions that might conflict with unseen code or alerting on variables not declared in the visible scope, as the context is incomplete.
{%- if extra_instructions %}
Extra instructions from the user, that should be taken into account with high priority:
Extra user-provided instructions (should be addressed with high priority):
======
{{ extra_instructions }}
======
@ -59,15 +65,16 @@ Extra instructions from the user, that should be taken into account with high pr
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 full file path of the relevant file")
language: str = Field(description="The programming language of the relevant file")
suggestion_content: str = Field(description="an actionable suggestion for meaningfully improving the new code introduced in the PR")
existing_code: str = Field(description="a short code snippet, demonstrating the relevant code lines from a '__new hunk__' section. It must be without line numbers. Quote only full code lines, not partial ones. Use abbreviations ("...") of full lines if needed")
improved_code: str = Field(description="a new code snippet, that can be used to replace the relevant 'existing_code' lines in '__new hunk__' 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.")
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', 'possible bug', 'possible issue', 'performance', 'enhancement', 'best practice', 'maintainability', etc. Other labels are also allowed")
relevant_file: str = Field(description="Full path of the relevant file")
language: str = Field(description="Programming language used by the relevant file")
suggestion_content: str = Field(description="An actionable suggestion to enhance, improve or fix the new code introduced in the PR. Don't present here actual code snippets, just the suggestion. Be short and concise")
existing_code: str = Field(description="A short code snippet from a '__new hunk__' section that the suggestion aims to enhance or fix. Include only complete code lines, without line numbers. Use ellipsis (...) for brevity if needed. This snippet should represent the specific PR code targeted for improvement.")
improved_code: str = Field(description="A refined code snippet that replaces the 'existing_code' snippet after implementing the suggestion.")
one_sentence_summary: str = Field(description="A concise, single-sentence overview of the suggested improvement. Focus on the 'what'. Be general, and avoid method or variable names.")
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 beginning of 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 end of the 'existing code' snippet above")
label: str = Field(description="A single, descriptive label that best characterizes the suggestion type. Possible labels include 'security', 'possible bug', 'possible issue', 'performance', 'enhancement', 'best practice', 'maintainability'. Other relevant labels are also acceptable.")
class PRCodeSuggestions(BaseModel):
code_suggestions: List[CodeSuggestion]
@ -112,106 +119,4 @@ The PR Diff:
Response (should be a valid YAML, and nothing else):
```yaml
"""
[pr_code_suggestions_prompt_claude]
system="""You are PR-Reviewer, a language model that specializes in suggesting improvements to a Pull Request (PR) code.
Your task is to provide meaningful and actionable code suggestions, to improve the new code presented in a PR code diff (lines starting with '+').
The format we will use to present the PR code diff:
======
## file: 'src/file1.py'
@@ ... @@ def func1():
__new hunk__
11 unchanged code line0 in the PR
12 unchanged code line1 in the PR
13 +new code line2 added in the PR
14 unchanged code line3 in the PR
__old hunk__
unchanged code line0
unchanged code line1
-old code line2 removed in the PR
unchanged code line3
@@ ... @@ def func2():
__new hunk__
...
__old hunk__
...
## file: 'src/file2.py'
...
======
- In this format, we separate each hunk of diff code to '__new hunk__' and '__old hunk__' sections. The '__new hunk__' section contains the new code of the chunk, and the '__old hunk__' section contains the old code, that was removed. If no new code was added in a specific hunk, '__new hunk__' section will not be presented. If no code was removed, '__old hunk__' section will not be presented.
- We also added line numbers for the '__new hunk__' code, to help you refer to the code lines in your suggestions. These line numbers are not part of the actual code, and should only used for reference.
- Code lines are prefixed with symbols ('+', '-', ' '). The '+' symbol indicates new code added in the PR, the '-' symbol indicates code removed in the PR, and the ' ' symbol indicates unchanged code. \
Specific instructions for generating code suggestions:
- Provide up to {{ num_code_suggestions }} code suggestions.
- The suggestions should be diverse and insightful. They should focus on improving only the new code introduced in the PR, meaning lines from '__new hunk__' sections, starting with '+' (after the line numbers).
- Prioritize suggestions that address possible issues, major problems, and bugs in the PR code. Don't repeat changes already present in the PR. If there are no relevant suggestions for the PR, return an empty list.
- Don't suggest to add docstring, type hints, or comments, or to remove unused imports.
- Provide the exact line numbers range (inclusive) for each suggestion. Use the line numbers from the '__new hunk__' sections.
- Every time you cite variables or names from the code, use backticks ('`'). For example: 'ensure that `variable_name` is ...'
- Take into account that you are recieving as an input only a PR code diff. The entire codebase is not available for you as context. Hence, avoid suggestions that might conflict with unseen parts of the codebase, like imports, global variables, etc.
{%- if extra_instructions %}
Extra instructions from the user, that should be taken into account with high priority:
======
{{ 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 full file path of the relevant file")
language: str = Field(description="the programming language of the relevant file")
suggestion_content: str = Field(description="an actionable suggestion for meaningfully improving the new code introduced in the PR. Don't present here actual code snippets, just the suggestion. Be short and concise")
existing_code: str = Field(description="a short code snippet, demonstrating the relevant code lines from a '__new hunk__' section. It must be without line numbers. Quote only full code lines, not partial ones. Use abbreviations ("...") of full lines if needed")
improved_code: str = Field(description="a new code snippet, that can be used to replace the relevant 'existing_code' lines in '__new hunk__' 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.")
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', 'possible bug', 'possible issue', 'performance', 'enhancement', '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: |
...
existing_code: |
...
improved_code: |
...
one_sentence_summary: |
...
relevant_lines_start: 12
relevant_lines_end: 13
label: |
...
```
Each YAML output MUST be after a newline, indented, with block scalar indicator ('|').
"""

View File

@ -1,32 +1,55 @@
[pr_code_suggestions_reflect_prompt]
system="""You are a language model that specializes in reviewing and evaluating suggestions for a Pull Request (PR) code.
system="""You are an AI language model specialized in reviewing and evaluating code suggestions for a Pull Request (PR).
Your task is to analyze a PR code diff and evaluate a set of AI-generated code suggestions. These suggestions aim to address potential bugs and problems, and enhance the new code introduced in the PR.
Your input is a PR code, and a list of code suggestions that were generated for the PR.
Your goal is to inspect, review and score the suggestsions.
Be aware - the suggestions may not always be correct or accurate, and you should evaluate them in relation to the actual PR code diff presented. Sometimes the suggestion may ignore parts of the actual code diff, and in that case, you should give it a score of 0.
Examine each suggestion meticulously, assessing its quality, relevance, and accuracy within the context of PR. Keep in mind that the suggestions may vary in their correctness and accuracy. Your evaluation should be based on a thorough comparison between each suggestion and the actual PR code diff.
Consider the following components of each suggestion:
1. 'one_sentence_summary' - A brief summary of the suggestion's purpose
2. 'suggestion_content' - The detailed suggestion content, explaining the proposed modification
3. 'existing_code' - a code snippet from a __new hunk__ section in the PR code diff that the suggestion addresses
4. 'improved_code' - a code snippet demonstrating how the 'existing_code' should be after the suggestion is applied
Specific instructions:
- Carefully review both the suggestion content, and the related PR code diff. Mistakes in the suggestions can occur. Make sure the suggestions are logical and correct, and properly derived from the PR code diff.
- In addition to the exact code lines mentioned in each suggestion, review the code around them, to ensure that the suggestions are contextually accurate.
- Check that the 'existing_code' field is valid. The 'existing_code' content should match, or be derived, from code lines from a 'new hunk' section in the PR code diff.
- Check that the 'improved_code' section correctly reflects the suggestion content.
- High scores (8 to 10) should be given to correct suggestions that address major bugs and issues, or security concerns. Lower scores (3 to 7) should be for correct suggestions addressing minor issues, code style, code readability, maintainability, etc. Don't give high scores to suggestions that are not crucial, and bring only small improvement or optimization.
- Order the feedback the same way the suggestions are ordered in the input.
Be particularly vigilant for suggestions that:
- Overlook crucial details in the PR
- The 'improved_code' section does not accurately reflect the suggested changes, in relation to the 'existing_code'
- Contradict or ignore parts of the PR's modifications
In such cases, assign the suggestion a score of 0.
For valid suggestions, your role is to provide an impartial and precise score assessment that accurately reflects each suggestion's potential impact on the PR's correctness, quality and functionality.
The format that is used to present the PR code diff is as follows:
Key guidelines for evaluation:
- Thoroughly examine both the suggestion content and the corresponding PR code diff. Be vigilant for potential errors in each suggestion, ensuring they are logically sound, accurate, and directly derived from the PR code diff.
- Extend your review beyond the specifically mentioned code lines to encompass surrounding context, verifying the suggestions' contextual accuracy.
- Validate the 'existing_code' field by confirming it matches or is accurately derived from code lines within a '__new hunk__' section of the PR code diff.
- Ensure the 'improved_code' section accurately reflects the 'existing_code' segment after the suggested modification is applied.
- Apply a nuanced scoring system:
- Reserve high scores (8-10) for suggestions addressing critical issues such as major bugs or security concerns.
- Assign moderate scores (3-7) to suggestions that tackle minor issues, improve code style, enhance readability, or boost maintainability.
- Avoid inflating scores for suggestions that, while correct, offer only marginal improvements or optimizations.
- Maintain the original order of suggestions in your feedback, corresponding to their input sequence.
The PR code diff will be presented in the following structured format:
======
## file: 'src/file1.py'
## File: 'src/file1.py'
{%- if is_ai_metadata %}
### AI-generated changes summary:
* ...
* ...
{%- endif %}
@@ ... @@ def func1():
__new hunk__
12 code line1 that remained unchanged in the PR
11 unchanged code line0 in the PR
12 unchanged code line1 in the PR
13 +new code line2 added in the PR
14 code line3 that remained unchanged in the PR
14 unchanged code line3 in the PR
__old hunk__
code line1 that remained unchanged in the PR
-old code line2 that was removed in the PR
code line3 that remained unchanged in the PR
unchanged code line0
unchanged code line1
-old code line2 removed in the PR
unchanged code line3
@@ ... @@ def func2():
__new hunk__
@ -35,22 +58,24 @@ __old hunk__
...
## file: 'src/file2.py'
## File: 'src/file2.py'
...
======
- In this format, we separated each hunk of code to '__new hunk__' and '__old hunk__' sections. The '__new hunk__' section contains the new code of the chunk, and the '__old hunk__' section contains the old code that was removed.
- If no new code was added in a specific hunk, '__new hunk__' section will not be presented. If no code was removed, '__old hunk__' section will not be presented.
- We added line numbers for the '__new hunk__' sections, to help you refer to the code lines in your suggestions. These line numbers are not part of the actual code, and are only used for reference.
- Code lines are prefixed symbols ('+', '-', ' '). The '+' symbol indicates new code added in the PR, the '-' symbol indicates code removed in the PR, and the ' ' symbol indicates unchanged code.
- In the format above, the diff is organized into separate '__new hunk__' and '__old hunk__' sections for each code chunk. '__new hunk__' contains the updated code, while '__old hunk__' shows the removed code. If no code was added or removed in a specific chunk, the corresponding section will be omitted.
- Line numbers are included for the '__new hunk__' sections to enable referencing specific lines in the code suggestions. These numbers are for reference only and are not part of the actual code.
- Code lines are prefixed with symbols: '+' for new code added in the PR, '-' for code removed, and ' ' for unchanged code.
{%- if is_ai_metadata %}
- When available, an AI-generated summary will precede each file's diff, with a high-level overview of the changes. Note that this summary may not be fully accurate or comprehensive.
{%- endif %}
The output must be a YAML object equivalent to type $PRCodeSuggestionsFeedback, according to the following Pydantic definitions:
=====
class CodeSuggestionFeedback(BaseModel):
suggestion_summary: str = Field(description="repeated from the input")
relevant_file: str = Field(description="repeated from the input")
suggestion_score: int = Field(description="The actual output - the score of the suggestion, from 0 to 10. Give 0 if the suggestion is wrong. Otherwise, give a score from 1 to 10 (inclusive), where 1 is the lowest and 10 is the highest.")
why: str = Field(description="Short and concise explanation of why the suggestion received the score (one to two sentences).")
suggestion_summary: str = Field(description="Repeated from the input")
relevant_file: str = Field(description="Repeated from the input")
suggestion_score: int = Field(description="Evaluate the suggestion and assign a score from 0 to 10. Give 0 if the suggestion is wrong. For valid suggestions, score from 1 (lowest impact/importance) to 10 (highest impact/importance).")
why: str = Field(description="Briefly explain the score given in 1-2 sentences, focusing on the suggestion's impact, relevance, and accuracy.")
class PRCodeSuggestionsFeedback(BaseModel):
code_suggestions: List[CodeSuggestionFeedback]
@ -79,7 +104,7 @@ user="""You are given a Pull Request (PR) code diff:
======
And here is a list of corresponding {{ num_code_suggestions }} code suggestions to improve this Pull Request code:
Below are {{ num_code_suggestions }} AI-generated code suggestions for enhancing the Pull Request:
======
{{ suggestion_str|trim }}
======

View File

@ -0,0 +1,48 @@
[pr_help_prompts]
system="""You are Doc-helper, a language models designed to answer questions about a documentation website for an open-soure project called "PR-Agent".
You will recieve a question, and a list of snippets that were collected for a documentation site using RAG as the retrieval method.
Your goal is to provide the best answer to the question using the snippets provided.
Additional instructions:
- Try to be short and concise in your answers. Give examples if needed.
- It is possible some of the snippets may not be relevant to the question. In that case, you should ignore them and focus on the ones that are relevant.
- The main tools of pr-agent are 'describe', 'review', 'improve'. If there is ambiguity to which tool the user is referring to, prioritize snippets of these tools over others.
The output must be a YAML object equivalent to type $DocHelper, according to the following Pydantic definitions:
=====
class DocHelper(BaseModel):
user_question: str = Field(description="The user's question")
response: str = Field(description="The response to the user's question")
relevant_snippets: List[int] = Field(description="One-based index of the relevant snippets in the list of snippets provided. Order the by relevance, with the most relevant first. If a snippet was not relevant, do not include it in the list.")
=====
Example output:
```yaml
user_question: |
...
response: |
...
relevant_snippets:
- 1
- 2
- 4
"""
user="""\
User's Question:
=====
{{ question|trim }}
=====
Relevant doc snippets retrieved:
=====
{{ snippets|trim }}
=====
Response (should be a valid YAML, and nothing else):
```yaml
"""

View File

@ -12,7 +12,7 @@ Additional guidelines:
Example Hunk Structure:
======
## file: 'src/file1.py'
## File: 'src/file1.py'
@@ -12,5 +12,5 @@ def func1():
code line 1 that remained unchanged in the PR

View File

@ -10,7 +10,13 @@ The review should focus on new code added in the PR code diff (lines starting wi
The format we will use to present the PR code diff:
======
## file: 'src/file1.py'
## File: 'src/file1.py'
{%- if is_ai_metadata %}
### AI-generated changes summary:
* ...
* ...
{%- endif %}
@@ ... @@ def func1():
__new hunk__
@ -31,7 +37,7 @@ __old hunk__
...
## file: 'src/file2.py'
## File: 'src/file2.py'
...
======
@ -39,6 +45,9 @@ __old hunk__
- We also added line numbers for the '__new hunk__' code, to help you refer to the code lines in your suggestions. These line numbers are not part of the actual code, and should only used for reference.
- Code lines are prefixed with symbols ('+', '-', ' '). The '+' symbol indicates new code added in the PR, the '-' symbol indicates code removed in the PR, and the ' ' symbol indicates unchanged code. \
The review should address new code added in the PR code diff (lines starting with '+')
{%- if is_ai_metadata %}
- If available, an AI-generated summary will appear and provide a high-level overview of the file changes. Note that this summary may not be fully accurate or complete.
{%- endif %}
- When quoting variables or names from the code, use backticks (`) instead of single quote (').
{%- if num_code_suggestions > 0 %}
@ -72,19 +81,10 @@ class SubPR(BaseModel):
class KeyIssuesComponentLink(BaseModel):
relevant_file: str = Field(description="The full file path of the relevant file")
issue_header: str = Field(description="one or two word title for the the issue. For example: 'Possible Bug', 'Performance Issue', 'Code Smell', etc.")
issue_content: str = Field(description="a short and concise description of the issue that needs to be reviewed")
start_line: int = Field(description="the start line that corresponds to this issue in the relevant file")
end_line: int = Field(description="the end line that corresponds to this issue in the relevant file")
{%- if extra_issue_links %}
referenced_variables: List[Refs] = Field(description="a list of relevant variables or names that appear in the 'issue_content' output. For each variable, output is name, and the line number where it appears in the relevant file")
{% endif %}
{%- if extra_issue_links %}
class Refs(BaseModel):
variable_name: str = Field(description="the name of a variable or name that appears in the relevant 'issue_content' output.")
relevant_line: int = Field(description="the line number where the variable or name appears in the relevant file")
{%- endif %}
issue_header: str = Field(description="One or two word title for the the issue. For example: 'Possible Bug', 'Performance Issue', 'Code Smell', etc.")
issue_content: str = Field(description="A short and concise summary of what should be further inspected and validated during the PR review process for this issue. Don't state line numbers here")
start_line: int = Field(description="The start line that corresponds to this issue in the relevant file")
end_line: int = Field(description="The end line that corresponds to this issue in the relevant file")
class Review(BaseModel):
{%- if require_estimate_effort_to_review %}
@ -99,7 +99,7 @@ class Review(BaseModel):
{%- if question_str %}
insights_from_user_answers: str = Field(description="shortly summarize the insights you gained from the user's answers to the questions")
{%- endif %}
key_issues_to_review: List[KeyIssuesComponentLink] = Field("A list of bugs, issue or major performance concerns introduced in this PR, which the PR reviewer should further investigate")
key_issues_to_review: List[KeyIssuesComponentLink] = Field("A diverse list of bugs, issue or major performance concerns introduced in this PR, which the PR reviewer should further investigate")
{%- if require_security_review %}
security_concerns: str = Field(description="Does this PR code introduce possible vulnerabilities such as exposure of sensitive information (e.g., API keys, secrets, passwords), or security concerns like SQL injection, XSS, CSRF, and others ? Answer 'No' (without explaining why) if there are no possible issues. If there are security concerns or issues, start your answer with a short header, such as: 'Sensitive information exposure: ...', 'SQL injection: ...' etc. Explain your answer. Be specific and give examples if possible")
{%- endif %}
@ -149,12 +149,6 @@ review:
...
start_line: 12
end_line: 14
{%- if extra_issue_links %}
referenced_variables:
- variable_name: |
...
relevant_line: 13
{%- endif %}
- ...
security_concerns: |
No

View File

@ -0,0 +1,5 @@
# Credits
These queries, and some of the logic, were adopted from the excellent [Aider](https://github.com/paul-gauthier/aider/blob/main/aider/queries/README.md) project.

View File

@ -0,0 +1,9 @@
(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class
(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class
(function_declarator declarator: (identifier) @name.definition.function) @definition.function
(type_definition declarator: (type_identifier) @name.definition.type) @definition.type
(enum_specifier name: (type_identifier) @name.definition.type) @definition.type

View File

@ -0,0 +1,46 @@
(class_declaration
name: (identifier) @name.definition.class
) @definition.class
(class_declaration
bases: (base_list (_) @name.reference.class)
) @reference.class
(interface_declaration
name: (identifier) @name.definition.interface
) @definition.interface
(interface_declaration
bases: (base_list (_) @name.reference.interface)
) @reference.interface
(method_declaration
name: (identifier) @name.definition.method
) @definition.method
(object_creation_expression
type: (identifier) @name.reference.class
) @reference.class
(type_parameter_constraints_clause
target: (identifier) @name.reference.class
) @reference.class
(type_constraint
type: (identifier) @name.reference.class
) @reference.class
(variable_declaration
type: (identifier) @name.reference.class
) @reference.class
(invocation_expression
function:
(member_access_expression
name: (identifier) @name.reference.send
)
) @reference.send
(namespace_declaration
name: (identifier) @name.definition.module
) @definition.module

View File

@ -0,0 +1,15 @@
(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class
(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class
(function_declarator declarator: (identifier) @name.definition.function) @definition.function
(function_declarator declarator: (field_identifier) @name.definition.function) @definition.function
(function_declarator declarator: (qualified_identifier scope: (namespace_identifier) @scope name: (identifier) @name.definition.method)) @definition.method
(type_definition declarator: (type_identifier) @name.definition.type) @definition.type
(enum_specifier name: (type_identifier) @name.definition.type) @definition.type
(class_specifier name: (type_identifier) @name.definition.class) @definition.class

View File

@ -0,0 +1,8 @@
;; defun/defsubst
(function_definition name: (symbol) @name.definition.function) @definition.function
;; Treat macros as function definitions for the sake of TAGS.
(macro_definition name: (symbol) @name.definition.function) @definition.function
;; Match function calls
(list (symbol) @name.reference.function) @reference.function

View File

@ -0,0 +1,54 @@
; Definitions
; * modules and protocols
(call
target: (identifier) @ignore
(arguments (alias) @name.definition.module)
(#match? @ignore "^(defmodule|defprotocol)$")) @definition.module
; * functions/macros
(call
target: (identifier) @ignore
(arguments
[
; zero-arity functions with no parentheses
(identifier) @name.definition.function
; regular function clause
(call target: (identifier) @name.definition.function)
; function clause with a guard clause
(binary_operator
left: (call target: (identifier) @name.definition.function)
operator: "when")
])
(#match? @ignore "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @definition.function
; References
; ignore calls to kernel/special-forms keywords
(call
target: (identifier) @ignore
(#match? @ignore "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defmodule|defprotocol|defimpl|defstruct|defexception|defoverridable|alias|case|cond|else|for|if|import|quote|raise|receive|require|reraise|super|throw|try|unless|unquote|unquote_splicing|use|with)$"))
; ignore module attributes
(unary_operator
operator: "@"
operand: (call
target: (identifier) @ignore))
; * function call
(call
target: [
; local
(identifier) @name.reference.call
; remote
(dot
right: (identifier) @name.reference.call)
]) @reference.call
; * pipe into function call
(binary_operator
operator: "|>"
right: (identifier) @name.reference.call) @reference.call
; * modules
(alias) @name.reference.module @reference.module

View File

@ -0,0 +1,19 @@
(value_declaration (function_declaration_left (lower_case_identifier) @name.definition.function)) @definition.function
(function_call_expr (value_expr (value_qid) @name.reference.function)) @reference.function
(exposed_value (lower_case_identifier) @name.reference.function) @reference.function
(type_annotation ((lower_case_identifier) @name.reference.function) (colon)) @reference.function
(type_declaration ((upper_case_identifier) @name.definition.type)) @definition.type
(type_ref (upper_case_qid (upper_case_identifier) @name.reference.type)) @reference.type
(exposed_type (upper_case_identifier) @name.reference.type) @reference.type
(type_declaration (union_variant (upper_case_identifier) @name.definition.union)) @definition.union
(value_expr (upper_case_qid (upper_case_identifier) @name.reference.union)) @reference.union
(module_declaration
(upper_case_qid (upper_case_identifier)) @name.definition.module
) @definition.module

View File

@ -0,0 +1,30 @@
(
(comment)* @doc
.
(function_declaration
name: (identifier) @name.definition.function) @definition.function
(#strip! @doc "^//\\s*")
(#set-adjacent! @doc @definition.function)
)
(
(comment)* @doc
.
(method_declaration
name: (field_identifier) @name.definition.method) @definition.method
(#strip! @doc "^//\\s*")
(#set-adjacent! @doc @definition.method)
)
(call_expression
function: [
(identifier) @name.reference.call
(parenthesized_expression (identifier) @name.reference.call)
(selector_expression field: (field_identifier) @name.reference.call)
(parenthesized_expression (selector_expression field: (field_identifier) @name.reference.call))
]) @reference.call
(type_spec
name: (type_identifier) @name.definition.type) @definition.type
(type_identifier) @name.reference.type @reference.type

View File

@ -0,0 +1,20 @@
(class_declaration
name: (identifier) @name.definition.class) @definition.class
(method_declaration
name: (identifier) @name.definition.method) @definition.method
(method_invocation
name: (identifier) @name.reference.call
arguments: (argument_list) @reference.call)
(interface_declaration
name: (identifier) @name.definition.interface) @definition.interface
(type_list
(type_identifier) @name.reference.implementation) @reference.implementation
(object_creation_expression
type: (type_identifier) @name.reference.class) @reference.class
(superclass (type_identifier) @name.reference.class) @reference.class

View File

@ -0,0 +1,88 @@
(
(comment)* @doc
.
(method_definition
name: (property_identifier) @name.definition.method) @definition.method
(#not-eq? @name.definition.method "constructor")
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
(#select-adjacent! @doc @definition.method)
)
(
(comment)* @doc
.
[
(class
name: (_) @name.definition.class)
(class_declaration
name: (_) @name.definition.class)
] @definition.class
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
(#select-adjacent! @doc @definition.class)
)
(
(comment)* @doc
.
[
(function
name: (identifier) @name.definition.function)
(function_declaration
name: (identifier) @name.definition.function)
(generator_function
name: (identifier) @name.definition.function)
(generator_function_declaration
name: (identifier) @name.definition.function)
] @definition.function
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
(#select-adjacent! @doc @definition.function)
)
(
(comment)* @doc
.
(lexical_declaration
(variable_declarator
name: (identifier) @name.definition.function
value: [(arrow_function) (function)]) @definition.function)
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
(#select-adjacent! @doc @definition.function)
)
(
(comment)* @doc
.
(variable_declaration
(variable_declarator
name: (identifier) @name.definition.function
value: [(arrow_function) (function)]) @definition.function)
(#strip! @doc "^[\\s\\*/]+|^[\\s\\*/]$")
(#select-adjacent! @doc @definition.function)
)
(assignment_expression
left: [
(identifier) @name.definition.function
(member_expression
property: (property_identifier) @name.definition.function)
]
right: [(arrow_function) (function)]
) @definition.function
(pair
key: (property_identifier) @name.definition.function
value: [(arrow_function) (function)]) @definition.function
(
(call_expression
function: (identifier) @name.reference.call) @reference.call
(#not-match? @name.reference.call "^(require)$")
)
(call_expression
function: (member_expression
property: (property_identifier) @name.reference.call)
arguments: (_) @reference.call)
(new_expression
constructor: (_) @name.reference.class) @reference.class

View File

@ -0,0 +1,115 @@
; Modules
;--------
(
(comment)? @doc .
(module_definition (module_binding (module_name) @name.definition.module) @definition.module)
(#strip! @doc "^\\(\\*\\*?\\s*|\\s\\*\\)$")
)
(module_path (module_name) @name.reference.module) @reference.module
; Module types
;--------------
(
(comment)? @doc .
(module_type_definition (module_type_name) @name.definition.interface) @definition.interface
(#strip! @doc "^\\(\\*\\*?\\s*|\\s\\*\\)$")
)
(module_type_path (module_type_name) @name.reference.implementation) @reference.implementation
; Functions
;----------
(
(comment)? @doc .
(value_definition
[
(let_binding
pattern: (value_name) @name.definition.function
(parameter))
(let_binding
pattern: (value_name) @name.definition.function
body: [(fun_expression) (function_expression)])
] @definition.function
)
(#strip! @doc "^\\(\\*\\*?\\s*|\\s\\*\\)$")
)
(
(comment)? @doc .
(external (value_name) @name.definition.function) @definition.function
(#strip! @doc "^\\(\\*\\*?\\s*|\\s\\*\\)$")
)
(application_expression
function: (value_path (value_name) @name.reference.call)) @reference.call
(infix_expression
left: (value_path (value_name) @name.reference.call)
operator: (concat_operator) @reference.call
(#eq? @reference.call "@@"))
(infix_expression
operator: (rel_operator) @reference.call
right: (value_path (value_name) @name.reference.call)
(#eq? @reference.call "|>"))
; Operator
;---------
(
(comment)? @doc .
(value_definition
(let_binding
pattern: (parenthesized_operator (_) @name.definition.function)) @definition.function)
(#strip! @doc "^\\(\\*\\*?\\s*|\\s\\*\\)$")
)
[
(prefix_operator)
(sign_operator)
(pow_operator)
(mult_operator)
(add_operator)
(concat_operator)
(rel_operator)
(and_operator)
(or_operator)
(assign_operator)
(hash_operator)
(indexing_operator)
(let_operator)
(let_and_operator)
(match_operator)
] @name.reference.call @reference.call
; Classes
;--------
(
(comment)? @doc .
[
(class_definition (class_binding (class_name) @name.definition.class) @definition.class)
(class_type_definition (class_type_binding (class_type_name) @name.definition.class) @definition.class)
]
(#strip! @doc "^\\(\\*\\*?\\s*|\\s\\*\\)$")
)
[
(class_path (class_name) @name.reference.class)
(class_type_path (class_type_name) @name.reference.class)
] @reference.class
; Methods
;--------
(
(comment)? @doc .
(method_definition (method_name) @name.definition.method) @definition.method
(#strip! @doc "^\\(\\*\\*?\\s*|\\s\\*\\)$")
)
(method_invocation (method_name) @name.reference.call) @reference.call

View File

@ -0,0 +1,26 @@
(class_declaration
name: (name) @name.definition.class) @definition.class
(function_definition
name: (name) @name.definition.function) @definition.function
(method_declaration
name: (name) @name.definition.function) @definition.function
(object_creation_expression
[
(qualified_name (name) @name.reference.class)
(variable_name (name) @name.reference.class)
]) @reference.class
(function_call_expression
function: [
(qualified_name (name) @name.reference.call)
(variable_name (name)) @name.reference.call
]) @reference.call
(scoped_call_expression
name: (name) @name.reference.call) @reference.call
(member_call_expression
name: (name) @name.reference.call) @reference.call

View File

@ -0,0 +1,12 @@
(class_definition
name: (identifier) @name.definition.class) @definition.class
(function_definition
name: (identifier) @name.definition.function) @definition.function
(call
function: [
(identifier) @name.reference.call
(attribute
attribute: (identifier) @name.reference.call)
]) @reference.call

View File

@ -0,0 +1,26 @@
(classlessPredicate
name: (predicateName) @name.definition.function) @definition.function
(memberPredicate
name: (predicateName) @name.definition.method) @definition.method
(aritylessPredicateExpr
name: (literalId) @name.reference.call) @reference.call
(module
name: (moduleName) @name.definition.module) @definition.module
(dataclass
name: (className) @name.definition.class) @definition.class
(datatype
name: (className) @name.definition.class) @definition.class
(datatypeBranch
name: (className) @name.definition.class) @definition.class
(qualifiedRhs
name: (predicateName) @name.reference.call) @reference.call
(typeExpr
name: (className) @name.reference.type) @reference.type

View File

@ -0,0 +1,64 @@
; Method definitions
(
(comment)* @doc
.
[
(method
name: (_) @name.definition.method) @definition.method
(singleton_method
name: (_) @name.definition.method) @definition.method
]
(#strip! @doc "^#\\s*")
(#select-adjacent! @doc @definition.method)
)
(alias
name: (_) @name.definition.method) @definition.method
(setter
(identifier) @ignore)
; Class definitions
(
(comment)* @doc
.
[
(class
name: [
(constant) @name.definition.class
(scope_resolution
name: (_) @name.definition.class)
]) @definition.class
(singleton_class
value: [
(constant) @name.definition.class
(scope_resolution
name: (_) @name.definition.class)
]) @definition.class
]
(#strip! @doc "^#\\s*")
(#select-adjacent! @doc @definition.class)
)
; Module definitions
(
(module
name: [
(constant) @name.definition.module
(scope_resolution
name: (_) @name.definition.module)
]) @definition.module
)
; Calls
(call method: (identifier) @name.reference.call) @reference.call
(
[(identifier) (constant)] @name.reference.call @reference.call
(#is-not? local)
(#not-match? @name.reference.call "^(lambda|load|require|require_relative|__FILE__|__LINE__)$")
)

View File

@ -0,0 +1,60 @@
; ADT definitions
(struct_item
name: (type_identifier) @name.definition.class) @definition.class
(enum_item
name: (type_identifier) @name.definition.class) @definition.class
(union_item
name: (type_identifier) @name.definition.class) @definition.class
; type aliases
(type_item
name: (type_identifier) @name.definition.class) @definition.class
; method definitions
(declaration_list
(function_item
name: (identifier) @name.definition.method)) @definition.method
; function definitions
(function_item
name: (identifier) @name.definition.function) @definition.function
; trait definitions
(trait_item
name: (type_identifier) @name.definition.interface) @definition.interface
; module definitions
(mod_item
name: (identifier) @name.definition.module) @definition.module
; macro definitions
(macro_definition
name: (identifier) @name.definition.macro) @definition.macro
; references
(call_expression
function: (identifier) @name.reference.call) @reference.call
(call_expression
function: (field_expression
field: (field_identifier) @name.reference.call)) @reference.call
(macro_invocation
macro: (identifier) @name.reference.call) @reference.call
; implementations
(impl_item
trait: (type_identifier) @name.reference.implementation) @reference.implementation
(impl_item
type: (type_identifier) @name.reference.implementation
!trait) @reference.implementation

View File

@ -0,0 +1,41 @@
(function_signature
name: (identifier) @name.definition.function) @definition.function
(method_signature
name: (property_identifier) @name.definition.method) @definition.method
(abstract_method_signature
name: (property_identifier) @name.definition.method) @definition.method
(abstract_class_declaration
name: (type_identifier) @name.definition.class) @definition.class
(module
name: (identifier) @name.definition.module) @definition.module
(interface_declaration
name: (type_identifier) @name.definition.interface) @definition.interface
(type_annotation
(type_identifier) @name.reference.type) @reference.type
(new_expression
constructor: (identifier) @name.reference.class) @reference.class
(function_declaration
name: (identifier) @name.definition.function) @definition.function
(method_definition
name: (property_identifier) @name.definition.method) @definition.method
(class_declaration
name: (type_identifier) @name.definition.class) @definition.class
(interface_declaration
name: (type_identifier) @name.definition.class) @definition.class
(type_alias_declaration
name: (type_identifier) @name.definition.type) @definition.type
(enum_declaration
name: (identifier) @name.definition.enum) @definition.enum

View File

@ -0,0 +1,150 @@
import os
from pathlib import Path
from grep_ast import TreeContext
from grep_ast.parsers import PARSERS
# from pygments.lexers import guess_lexer_for_filename
# from pygments.token import Token
from tree_sitter_languages import get_language, get_parser
def filename_to_lang(filename):
file_extension = os.path.splitext(filename)[0]
lang = PARSERS.get(file_extension)
return lang
class FileSummary:
"""
This class is used to summarize the content of a file using tree-sitter queries.
Supported languages: C, C++, C#, elisp, elixir, go, java, javascript, ocaml, php, python, ql, ruby, rust, typescript
"""
def __init__(self, fname_full_path: str, project_base_path, parent_context=True, child_context=False, header_max=0):
self.fname_full_path = fname_full_path
self.project_base_path = project_base_path
self.fname_rel = os.path.relpath(fname_full_path, project_base_path)
self.main_queries_path = Path(__file__).parent.parent // 'queries'
if not os.path.exists(fname_full_path):
print(f"File {fname_full_path} does not exist")
with open(fname_full_path, "w") as f:
code = f.read()
self.code = code.rstrip("\n") + "\n"
self.parent_context = parent_context
self.child_context = child_context
self.header_max = header_max
def summarize(self):
query_results = self.get_query_results()
summary_str = self.query_processing(query_results)
return summary_str
def render_file_summary(self, lines_of_interest: list):
code = self.code
fname_rel = self.fname_rel
context = TreeContext(
fname_rel,
code,
color=False,
line_number=True, # number the lines (1-indexed)
parent_context=self.parent_context,
child_context=self.child_context,
last_line=False,
margin=0,
mark_lois=False,
loi_pad=0,
header_max=self.header_max, # max number of lines to show in a function header
show_top_of_file_parent_scope=False,
)
context.lines_of_interest = set()
context.add_lines_of_interest(lines_of_interest)
context.add_context()
res = context.format()
return res
def query_processing(self, query_results: list):
if not query_results:
return ""
output = ""
def_lines = [q['line'] for q in query_results if q['kind'] == "def"]
output += "\n"
output += query_results[0]['fname'] + ":\n"
output += self.render_file_summary(def_lines)
return output
def get_queries_scheme(self, lang) -> str:
try:
# Load the relevant queries
path = os.path.join(self.main_queries_path, f"tree-sitter-{lang}-tags.scm")
with open(path, "r") as f:
return f.read()
except KeyError:
return 0
def get_query_results(self):
fname_rel = self.fname_rel
code = self.code
lang = filename_to_lang(fname_rel)
if not lang:
return
try:
language = get_language(lang)
parser = get_parser(lang)
except Exception as err:
print(f"Skipping file {fname_rel}: {err}")
return
query_scheme_str = self.get_queries_scheme(lang)
tree = parser.parse(bytes(code, "utf-8"))
# Run the queries
query = language.query(query_scheme_str)
captures = list(query.captures(tree.root_node))
# Parse the results into a list of "def" and "ref" tags
visited_set = set()
results = []
for node, tag in captures:
if tag.startswith("name.definition."):
kind = "ref"
elif tag.startswith("name.reference."):
kind = "def"
else:
continue
visited_set.add(kind)
result = dict(
fname=fname_rel,
name=node.text.decode("utf-8"),
kind=kind,
line=node.start_point[0],
)
results.append(result)
if "ref" in visited_set:
return results
if "def" not in visited_set:
return results
## currently we are interested only in defs
# # We saw defs, without any refs
# # Some files only provide defs (cpp, for example)
# # Use pygments to backfill refs
# try:
# lexer = guess_lexer_for_filename(fname, code)
# except Exception:
# return
#
# tokens = list(lexer.get_tokens(code))
# tokens = [token[1] for token in tokens if token[0] in Token.Name]
#
# for t in tokens:
# result = dict(
# fname=fname,
# name=t,
# kind="ref",
# line=-1,
# )
# results.append(result)
return results

View File

@ -0,0 +1,428 @@
// Taken from 'https://github.com/dolphin-emu/dolphin'
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <bit>
#include <memory>
#include <mbedtls/aes.h>
#include "Common/Assert.h"
#include "Common/CPUDetect.h"
#include "Common/Crypto/AES.h"
#ifdef _MSC_VER
#include <intrin.h>
#else
#if defined(_M_X86_64)
#include <x86intrin.h>
#elif defined(_M_ARM_64)
#include <arm_acle.h>
#include <arm_neon.h>
#endif
#endif
#ifdef _MSC_VER
#define ATTRIBUTE_TARGET(x)
#else
#define ATTRIBUTE_TARGET(x) [[gnu::target(x)]]
#endif
namespace Common::AES
{
// For x64 and arm64, it's very unlikely a user's cpu does not support the accelerated version,
// fallback is just in case.
template <Mode AesMode>
class ContextGeneric final : public Context
{
public:
ContextGeneric(const u8* key)
{
mbedtls_aes_init(&ctx);
if constexpr (AesMode == Mode::Encrypt)
ASSERT(!mbedtls_aes_setkey_enc(&ctx, key, 128));
else
ASSERT(!mbedtls_aes_setkey_dec(&ctx, key, 128));
}
virtual bool Crypt(const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out,
size_t len) const override
{
std::array<u8, BLOCK_SIZE> iv_tmp{};
if (iv)
std::memcpy(&iv_tmp[0], iv, BLOCK_SIZE);
constexpr int mode = (AesMode == Mode::Encrypt) ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT;
if (mbedtls_aes_crypt_cbc(const_cast<mbedtls_aes_context*>(&ctx), mode, len, &iv_tmp[0], buf_in,
buf_out))
return false;
if (iv_out)
std::memcpy(iv_out, &iv_tmp[0], BLOCK_SIZE);
return true;
}
private:
mbedtls_aes_context ctx{};
};
#if defined(_M_X86_64)
// Note that (for instructions with same data width) the actual instructions emitted vary depending
// on compiler and flags. The naming is somewhat confusing, because VAES cpuid flag was added after
// VAES(VEX.128):
// clang-format off
// instructions | cpuid flag | #define
// AES(128) | AES | -
// VAES(VEX.128) | AES & AVX | __AVX__
// VAES(VEX.256) | VAES | -
// VAES(EVEX.128) | VAES & AVX512VL | __AVX512VL__
// VAES(EVEX.256) | VAES & AVX512VL | __AVX512VL__
// VAES(EVEX.512) | VAES & AVX512F | __AVX512F__
// clang-format on
template <Mode AesMode>
class ContextAESNI final : public Context
{
static inline __m128i Aes128KeygenAssistFinish(__m128i key, __m128i kga)
{
__m128i tmp = _mm_shuffle_epi32(kga, _MM_SHUFFLE(3, 3, 3, 3));
tmp = _mm_xor_si128(tmp, key);
key = _mm_slli_si128(key, 4);
tmp = _mm_xor_si128(tmp, key);
key = _mm_slli_si128(key, 4);
tmp = _mm_xor_si128(tmp, key);
key = _mm_slli_si128(key, 4);
tmp = _mm_xor_si128(tmp, key);
return tmp;
}
template <size_t RoundIdx>
ATTRIBUTE_TARGET("aes")
inline constexpr void StoreRoundKey(__m128i rk)
{
if constexpr (AesMode == Mode::Encrypt)
round_keys[RoundIdx] = rk;
else
{
constexpr size_t idx = NUM_ROUND_KEYS - RoundIdx - 1;
if constexpr (idx == 0 || idx == NUM_ROUND_KEYS - 1)
round_keys[idx] = rk;
else
round_keys[idx] = _mm_aesimc_si128(rk);
}
}
template <size_t RoundIdx, int Rcon>
ATTRIBUTE_TARGET("aes")
inline constexpr __m128i Aes128Keygen(__m128i rk)
{
rk = Aes128KeygenAssistFinish(rk, _mm_aeskeygenassist_si128(rk, Rcon));
StoreRoundKey<RoundIdx>(rk);
return rk;
}
public:
ContextAESNI(const u8* key)
{
__m128i rk = _mm_loadu_si128((const __m128i*)key);
StoreRoundKey<0>(rk);
rk = Aes128Keygen<1, 0x01>(rk);
rk = Aes128Keygen<2, 0x02>(rk);
rk = Aes128Keygen<3, 0x04>(rk);
rk = Aes128Keygen<4, 0x08>(rk);
rk = Aes128Keygen<5, 0x10>(rk);
rk = Aes128Keygen<6, 0x20>(rk);
rk = Aes128Keygen<7, 0x40>(rk);
rk = Aes128Keygen<8, 0x80>(rk);
rk = Aes128Keygen<9, 0x1b>(rk);
Aes128Keygen<10, 0x36>(rk);
}
ATTRIBUTE_TARGET("aes")
inline void CryptBlock(__m128i* iv, const u8* buf_in, u8* buf_out) const
{
__m128i block = _mm_loadu_si128((const __m128i*)buf_in);
if constexpr (AesMode == Mode::Encrypt)
{
block = _mm_xor_si128(_mm_xor_si128(block, *iv), round_keys[0]);
for (size_t i = 1; i < Nr; ++i)
block = _mm_aesenc_si128(block, round_keys[i]);
block = _mm_aesenclast_si128(block, round_keys[Nr]);
*iv = block;
}
else
{
__m128i iv_next = block;
block = _mm_xor_si128(block, round_keys[0]);
for (size_t i = 1; i < Nr; ++i)
block = _mm_aesdec_si128(block, round_keys[i]);
block = _mm_aesdeclast_si128(block, round_keys[Nr]);
block = _mm_xor_si128(block, *iv);
*iv = iv_next;
}
_mm_storeu_si128((__m128i*)buf_out, block);
}
// Takes advantage of instruction pipelining to parallelize.
template <size_t NumBlocks>
ATTRIBUTE_TARGET("aes")
inline void DecryptPipelined(__m128i* iv, const u8* buf_in, u8* buf_out) const
{
constexpr size_t Depth = NumBlocks;
__m128i block[Depth];
for (size_t d = 0; d < Depth; d++)
block[d] = _mm_loadu_si128(&((const __m128i*)buf_in)[d]);
__m128i iv_next[1 + Depth];
iv_next[0] = *iv;
for (size_t d = 0; d < Depth; d++)
iv_next[1 + d] = block[d];
for (size_t d = 0; d < Depth; d++)
block[d] = _mm_xor_si128(block[d], round_keys[0]);
// The main speedup is here
for (size_t i = 1; i < Nr; ++i)
for (size_t d = 0; d < Depth; d++)
block[d] = _mm_aesdec_si128(block[d], round_keys[i]);
for (size_t d = 0; d < Depth; d++)
block[d] = _mm_aesdeclast_si128(block[d], round_keys[Nr]);
for (size_t d = 0; d < Depth; d++)
block[d] = _mm_xor_si128(block[d], iv_next[d]);
*iv = iv_next[1 + Depth - 1];
for (size_t d = 0; d < Depth; d++)
_mm_storeu_si128(&((__m128i*)buf_out)[d], block[d]);
}
virtual bool Crypt(const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out,
size_t len) const override
{
if (len % BLOCK_SIZE)
return false;
__m128i iv_block = iv ? _mm_loadu_si128((const __m128i*)iv) : _mm_setzero_si128();
if constexpr (AesMode == Mode::Decrypt)
{
// On amd zen2...(benchmark, not real-world):
// With AES(128) instructions, BLOCK_DEPTH results in following speedup vs. non-pipelined: 4:
// 18%, 8: 22%, 9: 26%, 10-15: 31%. 16: 8% (register exhaustion). With VAES(VEX.128), 10 gives
// 36% speedup vs. its corresponding baseline. VAES(VEX.128) is ~4% faster than AES(128). The
// result is similar on zen3.
// Zen3 in general is 20% faster than zen2 in aes, and VAES(VEX.256) is 35% faster than
// zen3/VAES(VEX.128).
// It seems like VAES(VEX.256) should be faster?
constexpr size_t BLOCK_DEPTH = 10;
constexpr size_t CHUNK_LEN = BLOCK_DEPTH * BLOCK_SIZE;
while (len >= CHUNK_LEN)
{
DecryptPipelined<BLOCK_DEPTH>(&iv_block, buf_in, buf_out);
buf_in += CHUNK_LEN;
buf_out += CHUNK_LEN;
len -= CHUNK_LEN;
}
}
len /= BLOCK_SIZE;
while (len--)
{
CryptBlock(&iv_block, buf_in, buf_out);
buf_in += BLOCK_SIZE;
buf_out += BLOCK_SIZE;
}
if (iv_out)
_mm_storeu_si128((__m128i*)iv_out, iv_block);
return true;
}
private:
std::array<__m128i, NUM_ROUND_KEYS> round_keys;
};
#endif
#if defined(_M_ARM_64)
template <Mode AesMode>
class ContextNeon final : public Context
{
public:
template <size_t RoundIdx>
inline constexpr void StoreRoundKey(const u32* rk)
{
const uint8x16_t rk_block = vreinterpretq_u8_u32(vld1q_u32(rk));
if constexpr (AesMode == Mode::Encrypt)
round_keys[RoundIdx] = rk_block;
else
{
constexpr size_t idx = NUM_ROUND_KEYS - RoundIdx - 1;
if constexpr (idx == 0 || idx == NUM_ROUND_KEYS - 1)
round_keys[idx] = rk_block;
else
round_keys[idx] = vaesimcq_u8(rk_block);
}
}
ContextNeon(const u8* key)
{
constexpr u8 rcon[]{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
std::array<u32, Nb * NUM_ROUND_KEYS> rk{};
// This uses a nice trick I've seen in wolfssl (not sure original author),
// which uses vaeseq_u8 to assist keygen.
// vaeseq_u8: op1 = SubBytes(ShiftRows(AddRoundKey(op1, op2)))
// given RotWord == ShiftRows for row 1 (rol(x,8))
// Probably not super fast (moves to/from vector regs constantly), but it is nice and simple.
std::memcpy(&rk[0], key, KEY_SIZE);
StoreRoundKey<0>(&rk[0]);
for (size_t i = 0; i < rk.size() - Nk; i += Nk)
{
const uint8x16_t enc = vaeseq_u8(vreinterpretq_u8_u32(vmovq_n_u32(rk[i + 3])), vmovq_n_u8(0));
const u32 temp = vgetq_lane_u32(vreinterpretq_u32_u8(enc), 0);
rk[i + 4] = rk[i + 0] ^ std::rotr(temp, 8) ^ rcon[i / Nk];
rk[i + 5] = rk[i + 4] ^ rk[i + 1];
rk[i + 6] = rk[i + 5] ^ rk[i + 2];
rk[i + 7] = rk[i + 6] ^ rk[i + 3];
// clang-format off
// Not great
const size_t rki = 1 + i / Nk;
switch (rki)
{
case 1: StoreRoundKey< 1>(&rk[Nk * rki]); break;
case 2: StoreRoundKey< 2>(&rk[Nk * rki]); break;
case 3: StoreRoundKey< 3>(&rk[Nk * rki]); break;
case 4: StoreRoundKey< 4>(&rk[Nk * rki]); break;
case 5: StoreRoundKey< 5>(&rk[Nk * rki]); break;
case 6: StoreRoundKey< 6>(&rk[Nk * rki]); break;
case 7: StoreRoundKey< 7>(&rk[Nk * rki]); break;
case 8: StoreRoundKey< 8>(&rk[Nk * rki]); break;
case 9: StoreRoundKey< 9>(&rk[Nk * rki]); break;
case 10: StoreRoundKey<10>(&rk[Nk * rki]); break;
}
// clang-format on
}
}
inline void CryptBlock(uint8x16_t* iv, const u8* buf_in, u8* buf_out) const
{
uint8x16_t block = vld1q_u8(buf_in);
if constexpr (AesMode == Mode::Encrypt)
{
block = veorq_u8(block, *iv);
for (size_t i = 0; i < Nr - 1; ++i)
block = vaesmcq_u8(vaeseq_u8(block, round_keys[i]));
block = vaeseq_u8(block, round_keys[Nr - 1]);
block = veorq_u8(block, round_keys[Nr]);
*iv = block;
}
else
{
uint8x16_t iv_next = block;
for (size_t i = 0; i < Nr - 1; ++i)
block = vaesimcq_u8(vaesdq_u8(block, round_keys[i]));
block = vaesdq_u8(block, round_keys[Nr - 1]);
block = veorq_u8(block, round_keys[Nr]);
block = veorq_u8(block, *iv);
*iv = iv_next;
}
vst1q_u8(buf_out, block);
}
virtual bool Crypt(const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out,
size_t len) const override
{
if (len % BLOCK_SIZE)
return false;
uint8x16_t iv_block = iv ? vld1q_u8(iv) : vmovq_n_u8(0);
len /= BLOCK_SIZE;
while (len--)
{
CryptBlock(&iv_block, buf_in, buf_out);
buf_in += BLOCK_SIZE;
buf_out += BLOCK_SIZE;
}
if (iv_out)
vst1q_u8(iv_out, iv_block);
return true;
}
private:
std::array<uint8x16_t, NUM_ROUND_KEYS> round_keys;
};
#endif
template <Mode AesMode>
std::unique_ptr<Context> CreateContext(const u8* key)
{
if (cpu_info.bAES)
{
#if defined(_M_X86_64)
#if defined(__AVX__)
// If compiler enables AVX, the intrinsics will generate VAES(VEX.128) instructions.
// In the future we may want to compile the code twice and explicitly override the compiler
// flags. There doesn't seem to be much performance difference between AES(128) and
// VAES(VEX.128) at the moment, though.
if (cpu_info.bAVX)
#endif
return std::make_unique<ContextAESNI<AesMode>>(key);
#elif defined(_M_ARM_64)
return std::make_unique<ContextNeon<AesMode>>(key);
#endif
}
return std::make_unique<ContextGeneric<AesMode>>(key);
}
std::unique_ptr<Context> CreateContextEncrypt(const u8* key)
{
return CreateContext<Mode::Encrypt>(key);
}
std::unique_ptr<Context> CreateContextDecrypt(const u8* key)
{
return CreateContext<Mode::Decrypt>(key);
}
// OFB encryption and decryption are the exact same. We don't encrypt though.
void CryptOFB(const u8* key, const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out, size_t size)
{
mbedtls_aes_context aes_ctx;
size_t iv_offset = 0;
std::array<u8, 16> iv_tmp{};
if (iv)
std::memcpy(&iv_tmp[0], iv, 16);
ASSERT(!mbedtls_aes_setkey_enc(&aes_ctx, key, 128));
mbedtls_aes_crypt_ofb(&aes_ctx, size, &iv_offset, &iv_tmp[0], buf_in, buf_out);
if (iv_out)
std::memcpy(iv_out, &iv_tmp[0], 16);
}
} // namespace Common::AES

View File

@ -0,0 +1,586 @@
package com.houarizegai.calculator.ui;
import com.houarizegai.calculator.theme.properties.Theme;
import com.houarizegai.calculator.theme.ThemeLoader;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.util.Map;
import java.util.regex.Pattern;
import java.awt.Color;
import javax.swing.*;
import static com.houarizegai.calculator.util.ColorUtil.hex2Color;
public class CalculatorUI {
private static final String FONT_NAME = "Comic Sans MS";
private static final String DOUBLE_OR_NUMBER_REGEX = "([-]?\\d+[.]\\d*)|(\\d+)|(-\\d+)";
private static final String APPLICATION_TITLE = "Calculator";
private static final int WINDOW_WIDTH = 410;
private static final int WINDOW_HEIGHT = 600;
private static final int BUTTON_WIDTH = 80;
private static final int BUTTON_HEIGHT = 70;
private static final int MARGIN_X = 20;
private static final int MARGIN_Y = 60;
private final JFrame window;
private JComboBox<String> comboCalculatorType;
private JComboBox<String> comboTheme;
private JTextField inputScreen;
private JButton btnC;
private JButton btnBack;
private JButton btnMod;
private JButton btnDiv;
private JButton btnMul;
private JButton btnSub;
private JButton btnAdd;
private JButton btn0;
private JButton btn1;
private JButton btn2;
private JButton btn3;
private JButton btn4;
private JButton btn5;
private JButton btn6;
private JButton btn7;
private JButton btn8;
private JButton btn9;
private JButton btnPoint;
private JButton btnEqual;
private JButton btnRoot;
private JButton btnPower;
private JButton btnLog;
private char selectedOperator = ' ';
private boolean go = true; // For calculate with Opt != (=)
private boolean addToDisplay = true; // Connect numbers in display
private double typedValue = 0;
private final Map<String, Theme> themesMap;
public CalculatorUI() {
themesMap = ThemeLoader.loadThemes();
window = new JFrame(APPLICATION_TITLE);
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setLocationRelativeTo(null);
int[] columns = {MARGIN_X, MARGIN_X + 90, MARGIN_X + 90 * 2, MARGIN_X + 90 * 3, MARGIN_X + 90 * 4};
int[] rows = {MARGIN_Y, MARGIN_Y + 100, MARGIN_Y + 100 + 80, MARGIN_Y + 100 + 80 * 2, MARGIN_Y + 100 + 80 * 3, MARGIN_Y + 100 + 80 * 4};
initInputScreen(columns, rows);
initButtons(columns, rows);
initCalculatorTypeSelector();
initThemeSelector();
window.setLayout(null);
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
public double calculate(double firstNumber, double secondNumber, char operator) {
switch (operator) {
case '+':
return firstNumber + secondNumber;
case '-':
return firstNumber - secondNumber;
case '*':
return firstNumber * secondNumber;
case '/':
return firstNumber / secondNumber;
case '%':
return firstNumber % secondNumber;
case '^':
return Math.pow(firstNumber, secondNumber);
default:
return secondNumber;
}
}
private void initThemeSelector() {
comboTheme = createComboBox(themesMap.keySet().toArray(new String[0]), 230, 30, "Theme");
comboTheme.addItemListener(event -> {
if (event.getStateChange() != ItemEvent.SELECTED)
return;
String selectedTheme = (String) event.getItem();
applyTheme(themesMap.get(selectedTheme));
});
if (themesMap.entrySet().iterator().hasNext()) {
applyTheme(themesMap.entrySet().iterator().next().getValue());
}
}
private void initInputScreen(int[] columns, int[] rows) {
inputScreen = new JTextField("0");
inputScreen.setBounds(columns[0], rows[0], 350, 70);
inputScreen.setEditable(false);
inputScreen.setBackground(Color.WHITE);
inputScreen.setFont(new Font(FONT_NAME, Font.PLAIN, 33));
window.add(inputScreen);
}
private void initCalculatorTypeSelector() {
comboCalculatorType = createComboBox(new String[]{"Standard", "Scientific"}, 20, 30, "Calculator type");
comboCalculatorType.addItemListener(event -> {
if (event.getStateChange() != ItemEvent.SELECTED)
return;
String selectedItem = (String) event.getItem();
switch (selectedItem) {
case "Standard":
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
btnRoot.setVisible(false);
btnPower.setVisible(false);
btnLog.setVisible(false);
break;
case "Scientific":
window.setSize(WINDOW_WIDTH + 80, WINDOW_HEIGHT);
btnRoot.setVisible(true);
btnPower.setVisible(true);
btnLog.setVisible(true);
break;
}
});
}
private void initButtons(int[] columns, int[] rows) {
btnC = createButton("C", columns[0], rows[1]);
btnC.addActionListener(event -> {
inputScreen.setText("0");
selectedOperator = ' ';
typedValue = 0;
});
btnBack = createButton("<-", columns[1], rows[1]);
btnBack.addActionListener(event -> {
String str = inputScreen.getText();
StringBuilder str2 = new StringBuilder();
for (int i = 0; i < (str.length() - 1); i++) {
str2.append(str.charAt(i));
}
if (str2.toString().equals("")) {
inputScreen.setText("0");
} else {
inputScreen.setText(str2.toString());
}
});
btnMod = createButton("%", columns[2], rows[1]);
btnMod.addActionListener(event -> {
if (!Pattern.matches(DOUBLE_OR_NUMBER_REGEX, inputScreen.getText()) || !go)
return;
typedValue = calculate(typedValue, Double.parseDouble(inputScreen.getText()), selectedOperator);
if (Pattern.matches("[-]?[\\d]+[.][0]*", String.valueOf(typedValue))) {
inputScreen.setText(String.valueOf((int) typedValue));
} else {
inputScreen.setText(String.valueOf(typedValue));
}
selectedOperator = '%';
go = false;
addToDisplay = false;
});
btnDiv = createButton("/", columns[3], rows[1]);
btnDiv.addActionListener(event -> {
if (!Pattern.matches(DOUBLE_OR_NUMBER_REGEX, inputScreen.getText()))
return;
if (go) {
typedValue = calculate(typedValue, Double.parseDouble(inputScreen.getText()), selectedOperator);
if (Pattern.matches("[-]?[\\d]+[.][0]*", String.valueOf(typedValue))) {
inputScreen.setText(String.valueOf((int) typedValue));
} else {
inputScreen.setText(String.valueOf(typedValue));
}
selectedOperator = '/';
go = false;
addToDisplay = false;
} else {
selectedOperator = '/';
}
});
btn7 = createButton("7", columns[0], rows[2]);
btn7.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("7");
} else {
inputScreen.setText(inputScreen.getText() + "7");
}
} else {
inputScreen.setText("7");
addToDisplay = true;
}
go = true;
});
btn8 = createButton("8", columns[1], rows[2]);
btn8.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("8");
} else {
inputScreen.setText(inputScreen.getText() + "8");
}
} else {
inputScreen.setText("8");
addToDisplay = true;
}
go = true;
});
btn9 = createButton("9", columns[2], rows[2]);
btn9.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("9");
} else {
inputScreen.setText(inputScreen.getText() + "9");
}
} else {
inputScreen.setText("9");
addToDisplay = true;
}
go = true;
});
btnMul = createButton("*", columns[3], rows[2]);
btnMul.addActionListener(event -> {
if (!Pattern.matches(DOUBLE_OR_NUMBER_REGEX, inputScreen.getText()))
return;
if (go) {
typedValue = calculate(typedValue, Double.parseDouble(inputScreen.getText()), selectedOperator);
if (Pattern.matches("[-]?[\\d]+[.][0]*", String.valueOf(typedValue))) {
inputScreen.setText(String.valueOf((int) typedValue));
} else {
inputScreen.setText(String.valueOf(typedValue));
}
selectedOperator = '*';
go = false;
addToDisplay = false;
} else {
selectedOperator = '*';
}
});
btn4 = createButton("4", columns[0], rows[3]);
btn4.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("4");
} else {
inputScreen.setText(inputScreen.getText() + "4");
}
} else {
inputScreen.setText("4");
addToDisplay = true;
}
go = true;
});
btn5 = createButton("5", columns[1], rows[3]);
btn5.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("5");
} else {
inputScreen.setText(inputScreen.getText() + "5");
}
} else {
inputScreen.setText("5");
addToDisplay = true;
}
go = true;
});
btn6 = createButton("6", columns[2], rows[3]);
btn6.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("6");
} else {
inputScreen.setText(inputScreen.getText() + "6");
}
} else {
inputScreen.setText("6");
addToDisplay = true;
}
go = true;
});
btnSub = createButton("-", columns[3], rows[3]);
btnSub.addActionListener(event -> {
if (!Pattern.matches(DOUBLE_OR_NUMBER_REGEX, inputScreen.getText()))
return;
if (go) {
typedValue = calculate(typedValue, Double.parseDouble(inputScreen.getText()), selectedOperator);
if (Pattern.matches("[-]?[\\d]+[.][0]*", String.valueOf(typedValue))) {
inputScreen.setText(String.valueOf((int) typedValue));
} else {
inputScreen.setText(String.valueOf(typedValue));
}
selectedOperator = '-';
go = false;
addToDisplay = false;
} else {
selectedOperator = '-';
}
});
btn1 = createButton("1", columns[0], rows[4]);
btn1.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("1");
} else {
inputScreen.setText(inputScreen.getText() + "1");
}
} else {
inputScreen.setText("1");
addToDisplay = true;
}
go = true;
});
btn2 = createButton("2", columns[1], rows[4]);
btn2.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("2");
} else {
inputScreen.setText(inputScreen.getText() + "2");
}
} else {
inputScreen.setText("2");
addToDisplay = true;
}
go = true;
});
btn3 = createButton("3", columns[2], rows[4]);
btn3.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("3");
} else {
inputScreen.setText(inputScreen.getText() + "3");
}
} else {
inputScreen.setText("3");
addToDisplay = true;
}
go = true;
});
btnAdd = createButton("+", columns[3], rows[4]);
btnAdd.addActionListener(event -> {
if (!Pattern.matches(DOUBLE_OR_NUMBER_REGEX, inputScreen.getText()))
return;
if (go) {
typedValue = calculate(typedValue, Double.parseDouble(inputScreen.getText()), selectedOperator);
if (Pattern.matches("[-]?[\\d]+[.][0]*", String.valueOf(typedValue))) {
inputScreen.setText(String.valueOf((int) typedValue));
} else {
inputScreen.setText(String.valueOf(typedValue));
}
selectedOperator = '+';
go = false;
addToDisplay = false;
} else {
selectedOperator = '+';
}
});
btnPoint = createButton(".", columns[0], rows[5]);
btnPoint.addActionListener(event -> {
if (addToDisplay) {
if (!inputScreen.getText().contains(".")) {
inputScreen.setText(inputScreen.getText() + ".");
}
} else {
inputScreen.setText("0.");
addToDisplay = true;
}
go = true;
});
btn0 = createButton("0", columns[1], rows[5]);
btn0.addActionListener(event -> {
if (addToDisplay) {
if (Pattern.matches("[0]*", inputScreen.getText())) {
inputScreen.setText("0");
} else {
inputScreen.setText(inputScreen.getText() + "0");
}
} else {
inputScreen.setText("0");
addToDisplay = true;
}
go = true;
});
btnEqual = createButton("=", columns[2], rows[5]);
btnEqual.addActionListener(event -> {
if (!Pattern.matches(DOUBLE_OR_NUMBER_REGEX, inputScreen.getText()))
return;
if (go) {
typedValue = calculate(typedValue, Double.parseDouble(inputScreen.getText()), selectedOperator);
if (Pattern.matches("[-]?[\\d]+[.][0]*", String.valueOf(typedValue))) {
inputScreen.setText(String.valueOf((int) typedValue));
} else {
inputScreen.setText(String.valueOf(typedValue));
}
selectedOperator = '=';
addToDisplay = false;
}
});
btnEqual.setSize(2 * BUTTON_WIDTH + 10, BUTTON_HEIGHT);
btnRoot = createButton("", columns[4], rows[1]);
btnRoot.addActionListener(event -> {
if (!Pattern.matches(DOUBLE_OR_NUMBER_REGEX, inputScreen.getText()))
return;
if (go) {
typedValue = Math.sqrt(Double.parseDouble(inputScreen.getText()));
if (Pattern.matches("[-]?[\\d]+[.][0]*", String.valueOf(typedValue))) {
inputScreen.setText(String.valueOf((int) typedValue));
} else {
inputScreen.setText(String.valueOf(typedValue));
}
selectedOperator = '√';
addToDisplay = false;
}
});
btnRoot.setVisible(false);
btnPower = createButton("pow", columns[4], rows[2]);
btnPower.addActionListener(event -> {
if (!Pattern.matches(DOUBLE_OR_NUMBER_REGEX, inputScreen.getText()))
return;
if (go) {
typedValue = calculate(typedValue, Double.parseDouble(inputScreen.getText()), selectedOperator);
if (Pattern.matches("[-]?[\\d]+[.][0]*", String.valueOf(typedValue))) {
inputScreen.setText(String.valueOf((int) typedValue));
} else {
inputScreen.setText(String.valueOf(typedValue));
}
selectedOperator = '^';
go = false;
addToDisplay = false;
} else {
selectedOperator = '^';
}
});
btnPower.setFont(new Font("Comic Sans MS", Font.PLAIN, 24));
btnPower.setVisible(false);
btnLog = createButton("ln", columns[4], rows[3]);
btnLog.addActionListener(event -> {
if (!Pattern.matches(DOUBLE_OR_NUMBER_REGEX, inputScreen.getText()))
return;
if (go) {
typedValue = Math.log(Double.parseDouble(inputScreen.getText()));
if (Pattern.matches("[-]?[\\d]+[.][0]*", String.valueOf(typedValue))) {
inputScreen.setText(String.valueOf((int) typedValue));
} else {
inputScreen.setText(String.valueOf(typedValue));
}
selectedOperator = 'l';
addToDisplay = false;
}
});
btnLog.setVisible(false);
}
private JComboBox<String> createComboBox(String[] items, int x, int y, String toolTip) {
JComboBox<String> combo = new JComboBox<>(items);
combo.setBounds(x, y, 140, 25);
combo.setToolTipText(toolTip);
combo.setCursor(new Cursor(Cursor.HAND_CURSOR));
window.add(combo);
return combo;
}
private JButton createButton(String label, int x, int y) {
JButton btn = new JButton(label);
btn.setBounds(x, y, BUTTON_WIDTH, BUTTON_HEIGHT);
btn.setFont(new Font("Comic Sans MS", Font.PLAIN, 28));
btn.setCursor(new Cursor(Cursor.HAND_CURSOR));
btn.setFocusable(false);
window.add(btn);
return btn;
}
private void applyTheme(Theme theme) {
window.getContentPane().setBackground(hex2Color(theme.getApplicationBackground()));
comboCalculatorType.setForeground(hex2Color(theme.getTextColor()));
comboTheme.setForeground(hex2Color(theme.getTextColor()));
inputScreen.setForeground(hex2Color(theme.getTextColor()));
btn0.setForeground(hex2Color(theme.getTextColor()));
btn1.setForeground(hex2Color(theme.getTextColor()));
btn2.setForeground(hex2Color(theme.getTextColor()));
btn3.setForeground(hex2Color(theme.getTextColor()));
btn4.setForeground(hex2Color(theme.getTextColor()));
btn5.setForeground(hex2Color(theme.getTextColor()));
btn6.setForeground(hex2Color(theme.getTextColor()));
btn7.setForeground(hex2Color(theme.getTextColor()));
btn8.setForeground(hex2Color(theme.getTextColor()));
btn9.setForeground(hex2Color(theme.getTextColor()));
btnPoint.setForeground(hex2Color(theme.getTextColor()));
btnC.setForeground(hex2Color(theme.getTextColor()));
btnBack.setForeground(hex2Color(theme.getTextColor()));
btnMod.setForeground(hex2Color(theme.getTextColor()));
btnDiv.setForeground(hex2Color(theme.getTextColor()));
btnMul.setForeground(hex2Color(theme.getTextColor()));
btnSub.setForeground(hex2Color(theme.getTextColor()));
btnAdd.setForeground(hex2Color(theme.getTextColor()));
btnRoot.setForeground(hex2Color(theme.getTextColor()));
btnLog.setForeground(hex2Color(theme.getTextColor()));
btnPower.setForeground(hex2Color(theme.getTextColor()));
btnEqual.setForeground(hex2Color(theme.getBtnEqualTextColor()));
comboCalculatorType.setBackground(hex2Color(theme.getApplicationBackground()));
comboTheme.setBackground(hex2Color(theme.getApplicationBackground()));
inputScreen.setBackground(hex2Color(theme.getApplicationBackground()));
btn0.setBackground(hex2Color(theme.getNumbersBackground()));
btn1.setBackground(hex2Color(theme.getNumbersBackground()));
btn2.setBackground(hex2Color(theme.getNumbersBackground()));
btn3.setBackground(hex2Color(theme.getNumbersBackground()));
btn4.setBackground(hex2Color(theme.getNumbersBackground()));
btn5.setBackground(hex2Color(theme.getNumbersBackground()));
btn6.setBackground(hex2Color(theme.getNumbersBackground()));
btn7.setBackground(hex2Color(theme.getNumbersBackground()));
btn8.setBackground(hex2Color(theme.getNumbersBackground()));
btn9.setBackground(hex2Color(theme.getNumbersBackground()));
btnPoint.setBackground(hex2Color(theme.getNumbersBackground()));
btnC.setBackground(hex2Color(theme.getOperatorBackground()));
btnBack.setBackground(hex2Color(theme.getOperatorBackground()));
btnMod.setBackground(hex2Color(theme.getOperatorBackground()));
btnDiv.setBackground(hex2Color(theme.getOperatorBackground()));
btnMul.setBackground(hex2Color(theme.getOperatorBackground()));
btnSub.setBackground(hex2Color(theme.getOperatorBackground()));
btnAdd.setBackground(hex2Color(theme.getOperatorBackground()));
btnRoot.setBackground(hex2Color(theme.getOperatorBackground()));
btnLog.setBackground(hex2Color(theme.getOperatorBackground()));
btnPower.setBackground(hex2Color(theme.getOperatorBackground()));
btnEqual.setBackground(hex2Color(theme.getBtnEqualBackground()));
}
}

View File

@ -0,0 +1,138 @@
// @ts-ignore
import { Pattern } from './types/Pattern'; // @ts-ignore
import { Match } from './types/Match'; // @ts-ignore
import * as symbols from './internals/symbols'; // @ts-ignore
import { matchPattern } from './internals/helpers'; // @ts-ignore
type MatchState<output> =
| { matched: true; value: output }
| { matched: false; value: undefined };
const unmatched: MatchState<never> = {
matched: false,
value: undefined,
};
/**
* `match` creates a **pattern matching expression**.
* * Use `.with(pattern, handler)` to pattern match on the input.
* * Use `.exhaustive()` or `.otherwise(() => defaultValue)` to end the expression and get the result.
*
* [Read the documentation for `match` on GitHub](https://github.com/gvergnaud/ts-pattern#match)
*
* @example
* declare let input: "A" | "B";
*
* return match(input)
* .with("A", () => "It's an A!")
* .with("B", () => "It's a B!")
* .exhaustive();
*
*/
export function match<const input, output = symbols.unset>(
value: input
): Match<input, output> {
return new MatchExpression(value, unmatched) as any;
}
/**
* This class represents a match expression. It follows the
* builder pattern, we chain methods to add features to the expression
* until we call `.exhaustive`, `.otherwise` or the unsafe `.run`
* method to execute it.
*
* The types of this class aren't public, the public type definition
* can be found in src/types/Match.ts.
*/
class MatchExpression<input, output> {
constructor(private input: input, private state: MatchState<output>) {}
with(...args: any[]): MatchExpression<input, output> {
if (this.state.matched) return this;
const handler: (selection: unknown, value: input) => output =
args[args.length - 1];
const patterns: Pattern<input>[] = [args[0]];
let predicate: ((value: input) => unknown) | undefined = undefined;
if (args.length === 3 && typeof args[1] === 'function') {
// case with guard as second argument
patterns.push(args[0]);
predicate = args[1];
} else if (args.length > 2) {
// case with several patterns
patterns.push(...args.slice(1, args.length - 1));
}
let hasSelections = false;
let selected: Record<string, unknown> = {};
const select = (key: string, value: unknown) => {
hasSelections = true;
selected[key] = value;
};
const matched =
patterns.some((pattern) => matchPattern(pattern, this.input, select)) &&
(predicate ? Boolean(predicate(this.input)) : true);
const selections = hasSelections
? symbols.anonymousSelectKey in selected
? selected[symbols.anonymousSelectKey]
: selected
: this.input;
const state = matched
? {
matched: true as const,
value: handler(selections, this.input),
}
: unmatched;
return new MatchExpression(this.input, state);
}
when(
predicate: (value: input) => unknown,
handler: (selection: input, value: input) => output
): MatchExpression<input, output> {
if (this.state.matched) return this;
const matched = Boolean(predicate(this.input));
return new MatchExpression<input, output>(
this.input,
matched
? { matched: true, value: handler(this.input, this.input) }
: unmatched
);
}
otherwise(handler: (value: input) => output): output {
if (this.state.matched) return this.state.value;
return handler(this.input);
}
exhaustive(): output {
return this.run();
}
run(): output {
if (this.state.matched) return this.state.value;
let displayedValue;
try {
displayedValue = JSON.stringify(this.input);
} catch (e) {
displayedValue = this.input;
}
throw new Error(
`Pattern matching error: no pattern matches value ${displayedValue}`
);
}
returnType() {
return this;
}
}

View File

@ -0,0 +1,43 @@
import os
from pathlib import Path
from pr_agent.static_analysis.src.file_summary import FileSummary
class TestFileSummary:
def setup_method(self):
self.base_path = Path(__file__).parent
self.project_base_path = Path(__file__).parent.parent.parent.parent
def test_file_summary_cpp(self):
fname = os.path.join(self.base_path, 'example_files/AES.cpp')
if not os.path.exists(fname):
print(f"File {fname} does not exist")
return False
fname_summary = FileSummary(fname, self.project_base_path, parent_context=False, child_context=False,
header_max=0)
output = fname_summary.summarize()
expected_output = '\npr_agent/static_analysis/tests/example_files/AES.cpp:\n...⋮...\n 32│namespace Common::AES\n 33│{\n...⋮...\n 36│template <Mode AesMode>\n 37│class ContextGeneric final : public Context\n 38│{\n 39│public:\n 40│ ContextGeneric(const u8* key)\n...⋮...\n 49│ virtual bool Crypt(const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out,\n...⋮...\n 70│#if defined(_M_X86_64)\n 71│\n...⋮...\n 84│template <Mode AesMode>\n 85│class ContextAESNI final : public Context\n 86│{\n 87│ static inline __m128i Aes128KeygenAssistFinish(__m128i key, __m128i kga)\n...⋮...\n103│ inline constexpr void StoreRoundKey(__m128i rk)\n...⋮...\n119│ inline constexpr __m128i Aes128Keygen(__m128i rk)\n...⋮...\n127│ ContextAESNI(const u8* key)\n...⋮...\n144│ inline void CryptBlock(__m128i* iv, const u8* buf_in, u8* buf_out) const\n...⋮...\n178│ inline void DecryptPipelined(__m128i* iv, const u8* buf_in, u8* buf_out) const\n...⋮...\n209│ virtual bool Crypt(const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out,\n...⋮...\n259│#if defined(_M_ARM_64)\n260│\n261│template <Mode AesMode>\n262│class ContextNeon final : public Context\n263│{\n264│public:\n265│ template <size_t RoundIdx>\n266│ inline constexpr void StoreRoundKey(const u32* rk)\n...⋮...\n281│ ContextNeon(const u8* key)\n...⋮...\n322│ inline void CryptBlock(uint8x16_t* iv, const u8* buf_in, u8* buf_out) const\n...⋮...\n353│ virtual bool Crypt(const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out,\n...⋮...\n381│template <Mode AesMode>\n382│std::unique_ptr<Context> CreateContext(const u8* key)\n...⋮...\n402│std::unique_ptr<Context> CreateContextEncrypt(const u8* key)\n...⋮...\n407│std::unique_ptr<Context> CreateContextDecrypt(const u8* key)\n...⋮...\n413│void CryptOFB(const u8* key, const u8* iv, u8* iv_out, const u8* buf_in, u8* buf_out, size_t size)\n...⋮...\n'
assert output == expected_output
def test_file_typescript(self):
fname = os.path.join(self.base_path, 'example_files/match.ts')
if not os.path.exists(fname):
print(f"File {fname} does not exist")
return False
fname_summary = FileSummary(fname, self.project_base_path, parent_context=False, child_context=False,
header_max=0)
output = fname_summary.summarize()
expected_output = '\npr_agent/static_analysis/tests/example_files/match.ts:\n...⋮...\n 6│type MatchState<output> =\n...⋮...\n 31│export function match<const input, output = symbols.unset>(\n...⋮...\n 46│class MatchExpression<input, output> {\n 47│ constructor(private input: input, private state: MatchState<output>) {}\n 48│\n 49│ with(...args: any[]): MatchExpression<input, output> {\n...⋮...\n 94│ when(\n...⋮...\n110│ otherwise(handler: (value: input) => output): output {\n...⋮...\n115│ exhaustive(): output {\n...⋮...\n119│ run(): output {\n...⋮...\n134│ returnType() {\n...⋮...\n'
assert output == expected_output
def test_file_java(self):
fname = os.path.join(self.base_path, 'example_files/calc.java')
if not os.path.exists(fname):
print(f"File {fname} does not exist")
return False
fname_summary = FileSummary(fname, self.project_base_path, parent_context=False, child_context=False,
header_max=0)
output = fname_summary.summarize()
expected_output = '\npr_agent/static_analysis/tests/example_files/calc.java:\n...⋮...\n 16│public class CalculatorUI {\n 17│\n...⋮...\n 84│ public double calculate(double firstNumber, double secondNumber, char operator) {\n...⋮...\n103│ private void initThemeSelector() {\n...⋮...\n118│ private void initInputScreen(int[] columns, int[] rows) {\n...⋮...\n127│ private void initCalculatorTypeSelector() {\n...⋮...\n151│ private void initButtons(int[] columns, int[] rows) {\n...⋮...\n510│ private JComboBox<String> createComboBox(String[] items, int x, int y, String toolTip) {\n...⋮...\n520│ private JButton createButton(String label, int x, int y) {\n...⋮...\n531│ private void applyTheme(Theme theme) {\n...⋮...\n'
assert output == expected_output

View File

@ -7,7 +7,8 @@ from jinja2 import Environment, StrictUndefined
from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler
from pr_agent.algo.ai_handlers.litellm_ai_handler import LiteLLMAIHandler
from pr_agent.algo.pr_processing import get_pr_diff, get_pr_multi_diffs, retry_with_fallback_models
from pr_agent.algo.pr_processing import get_pr_diff, get_pr_multi_diffs, retry_with_fallback_models, \
add_ai_metadata_to_diff_files
from pr_agent.algo.token_handler import TokenHandler
from pr_agent.algo.utils import load_yaml, replace_code_tags, ModelType, show_relevant_configurations
from pr_agent.config_loader import get_settings
@ -43,10 +44,8 @@ class PRCodeSuggestions:
self.is_extended = self._get_is_extended(args or [])
except:
self.is_extended = False
if self.is_extended:
num_code_suggestions = get_settings().pr_code_suggestions.num_code_suggestions_per_chunk
else:
num_code_suggestions = get_settings().pr_code_suggestions.num_code_suggestions
num_code_suggestions = get_settings().pr_code_suggestions.num_code_suggestions_per_chunk
self.ai_handler = ai_handler()
self.ai_handler.main_pr_language = self.main_language
@ -54,22 +53,29 @@ class PRCodeSuggestions:
self.prediction = None
self.pr_url = pr_url
self.cli_mode = cli_mode
self.pr_description, self.pr_description_files = (
self.git_provider.get_pr_description(split_changes_walkthrough=True))
if (self.pr_description_files and get_settings().get("config.is_auto_command", False) and
get_settings().get("config.enable_ai_metadata", False)):
add_ai_metadata_to_diff_files(self.git_provider, self.pr_description_files)
get_logger().debug(f"AI metadata added to the this command")
else:
get_settings().set("config.enable_ai_metadata", False)
get_logger().debug(f"AI metadata is disabled for this command")
self.vars = {
"title": self.git_provider.pr.title,
"branch": self.git_provider.get_pr_branch(),
"description": self.git_provider.get_pr_description(),
"description": self.pr_description,
"language": self.main_language,
"diff": "", # empty diff for initial calculation
"num_code_suggestions": num_code_suggestions,
"extra_instructions": get_settings().pr_code_suggestions.extra_instructions,
"commit_messages_str": self.git_provider.get_commit_messages(),
"relevant_best_practices": "",
"is_ai_metadata": get_settings().get("config.enable_ai_metadata", False),
}
if 'claude' in get_settings().config.model:
# prompt for Claude, with minor adjustments
self.pr_code_suggestions_prompt_system = get_settings().pr_code_suggestions_prompt_claude.system
else:
self.pr_code_suggestions_prompt_system = get_settings().pr_code_suggestions_prompt.system
self.pr_code_suggestions_prompt_system = get_settings().pr_code_suggestions_prompt.system
self.token_handler = TokenHandler(self.git_provider.pr,
self.vars,
@ -137,6 +143,9 @@ class PRCodeSuggestions:
pr_body += ' <!-- approve pr self-review -->'
# add usage guide
if (get_settings().pr_code_suggestions.enable_chat_text and get_settings().config.is_auto_command
and isinstance(self.git_provider, GithubProvider)):
pr_body += "\n\n>💡 Need additional feedback ? start a [PR chat](https://chromewebstore.google.com/detail/ephlnjeghhogofkifjloamocljapahnl) \n\n"
if get_settings().pr_code_suggestions.enable_help_text:
pr_body += "<hr>\n\n<details> <summary><strong>💡 Tool usage guide:</strong></summary><hr> \n\n"
pr_body += HelpMessage.get_improve_usage_guide()
@ -146,6 +155,7 @@ class PRCodeSuggestions:
if get_settings().get('config', {}).get('output_relevant_configurations', False):
pr_body += show_relevant_configurations(relevant_section='pr_code_suggestions')
# publish the PR comment
if get_settings().pr_code_suggestions.persistent_comment:
final_update_message = False
self.publish_persistent_comment_with_history(pr_body,
@ -161,10 +171,28 @@ class PRCodeSuggestions:
else:
self.git_provider.publish_comment(pr_body)
# dual publishing mode
if int(get_settings().pr_code_suggestions.dual_publishing_score_threshold) > 0:
data_above_threshold = {'code_suggestions': []}
try:
for suggestion in data['code_suggestions']:
if int(suggestion.get('score', 0)) >= int(get_settings().pr_code_suggestions.dual_publishing_score_threshold) \
and suggestion.get('improved_code'):
data_above_threshold['code_suggestions'].append(suggestion)
if not data_above_threshold['code_suggestions'][-1]['existing_code']:
get_logger().info(f'Identical existing and improved code for dual publishing found')
data_above_threshold['code_suggestions'][-1]['existing_code'] = suggestion[
'improved_code']
if data_above_threshold['code_suggestions']:
get_logger().info(
f"Publishing {len(data_above_threshold['code_suggestions'])} suggestions in dual publishing mode")
self.push_inline_code_suggestions(data_above_threshold)
except Exception as e:
get_logger().error(f"Failed to publish dual publishing suggestions, error: {e}")
else:
self.push_inline_code_suggestions(data)
if self.progress_response:
self.progress_response.delete()
self.git_provider.remove_comment(self.progress_response)
else:
get_logger().info('Code suggestions generated for PR, but not published since publish_output is False.')
except Exception as e:
@ -589,7 +617,6 @@ class PRCodeSuggestions:
if get_settings().pr_code_suggestions.final_clip_factor != 1:
max_len = max(
len(data_sorted),
get_settings().pr_code_suggestions.num_code_suggestions,
get_settings().pr_code_suggestions.num_code_suggestions_per_chunk,
)
new_len = int(0.5 + max_len * get_settings().pr_code_suggestions.final_clip_factor)
@ -610,14 +637,15 @@ class PRCodeSuggestions:
pr_body += "No suggestions found to improve this PR."
return pr_body
if get_settings().pr_code_suggestions.enable_intro_text and get_settings().config.is_auto_command:
pr_body += "Explore these optional code suggestions:\n\n"
language_extension_map_org = get_settings().language_extension_map_org
extension_to_language = {}
for language, extensions in language_extension_map_org.items():
for ext in extensions:
extension_to_language[ext] = language
pr_body = "## PR Code Suggestions ✨\n\n"
pr_body += "<table>"
header = f"Suggestion"
delta = 66
@ -678,7 +706,7 @@ class PRCodeSuggestions:
patch = "\n".join(patch_orig.splitlines()[5:]).strip('\n')
example_code = ""
example_code += f"```diff\n{patch}\n```\n"
example_code += f"```diff\n{patch.rstrip()}\n```\n"
if i == 0:
pr_body += f"""<td>\n\n"""
else:
@ -734,7 +762,8 @@ class PRCodeSuggestions:
variables = {'suggestion_list': suggestion_list,
'suggestion_str': suggestion_str,
"diff": patches_diff,
'num_code_suggestions': len(suggestion_list)}
'num_code_suggestions': len(suggestion_list),
"is_ai_metadata": get_settings().get("config.enable_ai_metadata", False)}
environment = Environment(undefined=StrictUndefined)
system_prompt_reflect = environment.from_string(
get_settings().pr_code_suggestions_reflect_prompt.system).render(

View File

@ -1,3 +1,5 @@
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
@ -28,20 +30,33 @@ class PRConfig:
return ""
def _prepare_pr_configs(self) -> str:
import tomli
with open(get_settings().find_file("configuration.toml"), "rb") as conf_file:
configuration_headers = [header.lower() for header in tomli.load(conf_file).keys()]
conf_file = get_settings().find_file("configuration.toml")
conf_settings = Dynaconf(settings_files=[conf_file])
configuration_headers = [header.lower() for header in conf_settings.keys()]
relevant_configs = {
header: configs for header, configs in get_settings().to_dict().items()
if header.lower().startswith("pr_") and header.lower() in configuration_headers
if (header.lower().startswith("pr_") or header.lower().startswith("config")) and header.lower() in configuration_headers
}
comment_str = "Possible Configurations:"
skip_keys = ['ai_disclaimer', 'ai_disclaimer_title', 'ANALYTICS_FOLDER', 'secret_provider', "skip_keys",
'trial_prefix_message', 'no_eligible_message', 'identity_provider', 'ALLOWED_REPOS',
'APP_NAME']
extra_skip_keys = get_settings().config.get('config.skip_keys', [])
if extra_skip_keys:
skip_keys.extend(extra_skip_keys)
markdown_text = "<details> <summary><strong>🛠️ PR-Agent Configurations:</strong></summary> \n\n"
markdown_text += f"\n\n```yaml\n\n"
for header, configs in relevant_configs.items():
if configs:
comment_str += "\n"
markdown_text += "\n\n"
markdown_text += f"==================== {header} ===================="
for key, value in configs.items():
comment_str += f"\n{header.lower()}.{key.lower()} = {repr(value) if isinstance(value, str) else value}"
comment_str += " "
if get_settings().config.verbosity_level >= 2:
get_logger().info(f"comment_str:\n{comment_str}")
return comment_str
if key in skip_keys:
continue
markdown_text += f"\n{header.lower()}.{key.lower()} = {repr(value) if isinstance(value, str) else value}"
markdown_text += " "
markdown_text += "\n```"
markdown_text += "\n</details>\n"
get_logger().info(f"Possible Configurations outputted to PR comment", artifact=markdown_text)
return markdown_text

View File

@ -117,9 +117,8 @@ class PRDescription:
pr_body += "<hr>\n\n<details> <summary><strong>✨ Describe tool usage guide:</strong></summary><hr> \n\n"
pr_body += HelpMessage.get_describe_usage_guide()
pr_body += "\n</details>\n"
elif self.git_provider.is_supported("gfm_markdown") and get_settings().pr_description.enable_help_comment:
pr_body += "\n\n___\n\n> 💡 **PR-Agent usage**:"
pr_body += "\n>Comment `/help` on the PR to get a list of all available PR-Agent tools and their descriptions\n\n"
elif get_settings().pr_description.enable_help_comment:
pr_body += '\n\n___\n\n> 💡 **PR-Agent usage**: Comment `/help "your question"` on any pull request to receive relevant information'
# Output the relevant configurations if enabled
if get_settings().get('config', {}).get('output_relevant_configurations', False):
@ -638,9 +637,10 @@ def insert_br_after_x_chars(text, x=70):
text = replace_code_tags(text)
# convert list items to <li>
if text.startswith("- "):
if text.startswith("- ") or text.startswith("* "):
text = "<li>" + text[2:]
text = text.replace("\n- ", '<br><li> ').replace("\n - ", '<br><li> ')
text = text.replace("\n* ", '<br><li> ').replace("\n * ", '<br><li> ')
# convert new lines to <br>
text = text.replace("\n", '<br>')

View File

@ -1,106 +1,360 @@
import os
import traceback
import zipfile
import tempfile
import copy
from functools import partial
from jinja2 import Environment, StrictUndefined
from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler
from pr_agent.algo.ai_handlers.litellm_ai_handler import LiteLLMAIHandler
from pr_agent.algo.pr_processing import retry_with_fallback_models
from pr_agent.algo.token_handler import TokenHandler
from pr_agent.algo.utils import ModelType, load_yaml
from pr_agent.config_loader import get_settings
from pr_agent.git_providers import get_git_provider, GithubProvider
from pr_agent.git_providers import get_git_provider, GithubProvider, BitbucketServerProvider, \
get_git_provider_with_context
from pr_agent.log import get_logger
def extract_header(snippet):
res = ''
lines = snippet.split('===Snippet content===')[0].split('\n')
highest_header = ''
highest_level = float('inf')
for line in lines[::-1]:
line = line.strip()
if line.startswith('Header '):
highest_header = line.split(': ')[1]
if highest_header:
res = f"#{highest_header.lower().replace(' ', '-')}"
return res
class PRHelpMessage:
def __init__(self, pr_url: str, args=None, ai_handler=None):
self.git_provider = get_git_provider()(pr_url)
def __init__(self, pr_url: str, args=None, ai_handler: partial[BaseAiHandler,] = LiteLLMAIHandler):
self.git_provider = get_git_provider_with_context(pr_url)
self.ai_handler = ai_handler()
self.question_str = self.parse_args(args)
self.num_retrieved_snippets = get_settings().get('pr_help.num_retrieved_snippets', 5)
if self.question_str:
self.vars = {
"question": self.question_str,
"snippets": "",
}
self.token_handler = TokenHandler(None,
self.vars,
get_settings().pr_help_prompts.system,
get_settings().pr_help_prompts.user)
async def _prepare_prediction(self, model: str):
try:
variables = copy.deepcopy(self.vars)
environment = Environment(undefined=StrictUndefined)
system_prompt = environment.from_string(get_settings().pr_help_prompts.system).render(variables)
user_prompt = environment.from_string(get_settings().pr_help_prompts.user).render(variables)
response, finish_reason = await self.ai_handler.chat_completion(
model=model, temperature=get_settings().config.temperature, system=system_prompt, user=user_prompt)
return response
except Exception as e:
get_logger().error(f"Error while preparing prediction: {e}")
return ""
def parse_args(self, args):
if args and len(args) > 0:
question_str = " ".join(args)
else:
question_str = ""
return question_str
def get_sim_results_from_s3_db(self, embeddings):
get_logger().info("Loading the S3 index...")
sim_results = []
try:
from langchain_chroma import Chroma
from urllib import request
with tempfile.TemporaryDirectory() as temp_dir:
# Define the local file path within the temporary directory
local_file_path = os.path.join(temp_dir, 'chroma_db.zip')
bucket = 'pr-agent'
file_name = 'chroma_db.zip'
s3_url = f'https://{bucket}.s3.amazonaws.com/{file_name}'
request.urlretrieve(s3_url, local_file_path)
# # Download the file from S3 to the temporary directory
# s3 = boto3.client('s3')
# s3.download_file(bucket, file_name, local_file_path)
# Extract the contents of the zip file
with zipfile.ZipFile(local_file_path, 'r') as zip_ref:
zip_ref.extractall(temp_dir)
vectorstore = Chroma(persist_directory=temp_dir + "/chroma_db",
embedding_function=embeddings)
sim_results = vectorstore.similarity_search_with_score(self.question_str, k=self.num_retrieved_snippets)
except Exception as e:
get_logger().error(f"Error while getting sim from S3: {e}",
artifact={"traceback": traceback.format_exc()})
return sim_results
def get_sim_results_from_local_db(self, embeddings):
get_logger().info("Loading the local index...")
sim_results = []
try:
from langchain_chroma import Chroma
get_logger().info("Loading the Chroma index...")
db_path = "./docs/chroma_db.zip"
if not os.path.exists(db_path):
db_path= "/app/docs/chroma_db.zip"
if not os.path.exists(db_path):
get_logger().error("Local db not found")
return sim_results
with tempfile.TemporaryDirectory() as temp_dir:
# Extract the ZIP file
with zipfile.ZipFile(db_path, 'r') as zip_ref:
zip_ref.extractall(temp_dir)
vectorstore = Chroma(persist_directory=temp_dir + "/chroma_db",
embedding_function=embeddings)
# Do similarity search
sim_results = vectorstore.similarity_search_with_score(self.question_str, k=self.num_retrieved_snippets)
except Exception as e:
get_logger().error(f"Error while getting sim from local db: {e}",
artifact={"traceback": traceback.format_exc()})
return sim_results
def get_sim_results_from_pinecone_db(self, embeddings):
get_logger().info("Loading the Pinecone index...")
sim_results = []
try:
from langchain_pinecone import PineconeVectorStore
INDEX_NAME = "pr-agent-docs"
vectorstore = PineconeVectorStore(
index_name=INDEX_NAME, embedding=embeddings,
pinecone_api_key=get_settings().pinecone.api_key
)
# Do similarity search
sim_results = vectorstore.similarity_search_with_score(self.question_str, k=self.num_retrieved_snippets)
except Exception as e:
get_logger().error(f"Error while getting sim from Pinecone db: {e}",
artifact={"traceback": traceback.format_exc()})
return sim_results
async def run(self):
try:
if not self.git_provider.is_supported("gfm_markdown"):
self.git_provider.publish_comment(
"The `Help` tool requires gfm markdown, which is not supported by your code platform.")
return
if self.question_str:
get_logger().info(f'Answering a PR question about the PR {self.git_provider.pr_url} ')
get_logger().info('Getting PR Help Message...')
relevant_configs = {'pr_help': dict(get_settings().pr_help),
'config': dict(get_settings().config)}
get_logger().debug("Relevant configs", artifacts=relevant_configs)
pr_comment = "## PR Agent Walkthrough 🤖\n\n"
pr_comment += "Welcome to the PR Agent, an AI-powered tool for automated pull request analysis, feedback, suggestions and more."""
pr_comment += "\n\nHere is a list of tools you can use to interact with the PR Agent:\n"
base_path = "https://pr-agent-docs.codium.ai/tools"
if not get_settings().get('openai.key'):
if get_settings().config.publish_output:
self.git_provider.publish_comment(
"The `Help` tool chat feature requires an OpenAI API key for calculating embeddings")
else:
get_logger().error("The `Help` tool chat feature requires an OpenAI API key for calculating embeddings")
return
tool_names = []
tool_names.append(f"[DESCRIBE]({base_path}/describe/)")
tool_names.append(f"[REVIEW]({base_path}/review/)")
tool_names.append(f"[IMPROVE]({base_path}/improve/)")
tool_names.append(f"[UPDATE CHANGELOG]({base_path}/update_changelog/)")
tool_names.append(f"[ADD DOCS]({base_path}/documentation/) 💎")
tool_names.append(f"[TEST]({base_path}/test/) 💎")
tool_names.append(f"[IMPROVE COMPONENT]({base_path}/improve_component/) 💎")
tool_names.append(f"[ANALYZE]({base_path}/analyze/) 💎")
tool_names.append(f"[ASK]({base_path}/ask/)")
tool_names.append(f"[GENERATE CUSTOM LABELS]({base_path}/custom_labels/) 💎")
tool_names.append(f"[CI FEEDBACK]({base_path}/ci_feedback/) 💎")
tool_names.append(f"[CUSTOM PROMPT]({base_path}/custom_prompt/) 💎")
tool_names.append(f"[SIMILAR ISSUE]({base_path}/similar_issues/)")
# Initialize embeddings
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small",
api_key=get_settings().openai.key)
descriptions = []
descriptions.append("Generates PR description - title, type, summary, code walkthrough and labels")
descriptions.append("Adjustable feedback about the PR, possible issues, security concerns, review effort and more")
descriptions.append("Code suggestions for improving the PR")
descriptions.append("Automatically updates the changelog")
descriptions.append("Generates documentation to methods/functions/classes that changed in the PR")
descriptions.append("Generates unit tests for a specific component, based on the PR code change")
descriptions.append("Code suggestions for a specific component that changed in the PR")
descriptions.append("Identifies code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component")
descriptions.append("Answering free-text questions about the PR")
descriptions.append("Generates custom labels for the PR, based on specific guidelines defined by the user")
descriptions.append("Generates feedback and analysis for a failed CI job")
descriptions.append("Generates custom suggestions for improving the PR code, derived only from a specific guidelines prompt defined by the user")
descriptions.append("Automatically retrieves and presents similar issues")
# Get similar snippets via similarity search
if get_settings().pr_help.force_local_db:
sim_results = self.get_sim_results_from_local_db(embeddings)
elif get_settings().get('pinecone.api_key'):
sim_results = self.get_sim_results_from_pinecone_db(embeddings)
else:
sim_results = self.get_sim_results_from_s3_db(embeddings)
if not sim_results:
get_logger().info("Failed to load the S3 index. Loading the local index...")
sim_results = self.get_sim_results_from_local_db(embeddings)
if not sim_results:
get_logger().error("Failed to retrieve similar snippets. Exiting...")
return
commands =[]
commands.append("`/describe`")
commands.append("`/review`")
commands.append("`/improve`")
commands.append("`/update_changelog`")
commands.append("`/add_docs`")
commands.append("`/test`")
commands.append("`/improve_component`")
commands.append("`/analyze`")
commands.append("`/ask`")
commands.append("`/generate_labels`")
commands.append("`/checks`")
commands.append("`/custom_prompt`")
commands.append("`/similar_issue`")
# Prepare relevant snippets
relevant_pages_full, relevant_snippets_full_header, relevant_snippets_str =\
await self.prepare_relevant_snippets(sim_results)
self.vars['snippets'] = relevant_snippets_str.strip()
checkbox_list = []
checkbox_list.append(" - [ ] Run <!-- /describe -->")
checkbox_list.append(" - [ ] Run <!-- /review -->")
checkbox_list.append(" - [ ] Run <!-- /improve -->")
checkbox_list.append(" - [ ] Run <!-- /update_changelog -->")
checkbox_list.append(" - [ ] Run <!-- /add_docs -->")
checkbox_list.append(" - [ ] Run <!-- /test -->")
checkbox_list.append(" - [ ] Run <!-- /improve_component -->")
checkbox_list.append(" - [ ] Run <!-- /analyze -->")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
# run the AI model
response = await retry_with_fallback_models(self._prepare_prediction, model_type=ModelType.REGULAR)
response_yaml = load_yaml(response)
response_str = response_yaml.get('response')
relevant_snippets_numbers = response_yaml.get('relevant_snippets')
if isinstance(self.git_provider, GithubProvider) and not get_settings().config.get('disable_checkboxes', False):
pr_comment += f"<table><tr align='left'><th align='left'>Tool</th><th align='left'>Description</th><th align='left'>Trigger Interactively :gem:</th></tr>"
for i in range(len(tool_names)):
pr_comment += f"\n<tr><td align='left'>\n\n<strong>{tool_names[i]}</strong></td>\n<td>{descriptions[i]}</td>\n<td>\n\n{checkbox_list[i]}\n</td></tr>"
pr_comment += "</table>\n\n"
pr_comment += f"""\n\n(1) Note that each tool be [triggered automatically](https://pr-agent-docs.codium.ai/usage-guide/automations_and_usage/#github-app-automatic-tools-when-a-new-pr-is-opened) when a new PR is opened, or called manually by [commenting on a PR](https://pr-agent-docs.codium.ai/usage-guide/automations_and_usage/#online-usage)."""
pr_comment += f"""\n\n(2) Tools marked with [*] require additional parameters to be passed. For example, to invoke the `/ask` tool, you need to comment on a PR: `/ask "<question content>"`. See the relevant documentation for each tool for more details."""
if not relevant_snippets_numbers:
get_logger().info(f"Could not find relevant snippets for the question: {self.question_str}")
if get_settings().config.publish_output:
answer_str = f"### Question: \n{self.question_str}\n\n"
answer_str += f"### Answer:\n\n"
answer_str += f"Could not find relevant information to answer the question. Please provide more details and try again."
self.git_provider.publish_comment(answer_str)
return ""
# prepare the answer
answer_str = ""
if response_str:
answer_str += f"### Question: \n{self.question_str}\n\n"
answer_str += f"### Answer:\n{response_str.strip()}\n\n"
answer_str += f"#### Relevant Sources:\n\n"
paged_published = []
for page in relevant_snippets_numbers:
page = int(page - 1)
if page < len(relevant_pages_full) and page >= 0:
if relevant_pages_full[page] in paged_published:
continue
link = f"{relevant_pages_full[page]}{relevant_snippets_full_header[page]}"
# answer_str += f"> - [{relevant_pages_full[page]}]({link})\n"
answer_str += f"> - {link}\n"
paged_published.append(relevant_pages_full[page])
# publish the answer
if get_settings().config.publish_output:
self.git_provider.publish_comment(answer_str)
else:
get_logger().info(f"Answer:\n{answer_str}")
else:
pr_comment += f"<table><tr align='left'><th align='left'>Tool</th><th align='left'>Command</th><th align='left'>Description</th></tr>"
for i in range(len(tool_names)):
pr_comment += f"\n<tr><td align='left'>\n\n<strong>{tool_names[i]}</strong></td><td>{commands[i]}</td><td>{descriptions[i]}</td></tr>"
pr_comment += "</table>\n\n"
pr_comment += f"""\n\nNote that each tool be [invoked automatically](https://pr-agent-docs.codium.ai/usage-guide/automations_and_usage/) when a new PR is opened, or called manually by [commenting on a PR](https://pr-agent-docs.codium.ai/usage-guide/automations_and_usage/#online-usage)."""
if get_settings().config.publish_output:
self.git_provider.publish_comment(pr_comment)
if not isinstance(self.git_provider, BitbucketServerProvider) and not self.git_provider.is_supported("gfm_markdown"):
self.git_provider.publish_comment(
"The `Help` tool requires gfm markdown, which is not supported by your code platform.")
return
get_logger().info('Getting PR Help Message...')
relevant_configs = {'pr_help': dict(get_settings().pr_help),
'config': dict(get_settings().config)}
get_logger().debug("Relevant configs", artifacts=relevant_configs)
pr_comment = "## PR Agent Walkthrough 🤖\n\n"
pr_comment += "Welcome to the PR Agent, an AI-powered tool for automated pull request analysis, feedback, suggestions and more."""
pr_comment += "\n\nHere is a list of tools you can use to interact with the PR Agent:\n"
base_path = "https://pr-agent-docs.codium.ai/tools"
tool_names = []
tool_names.append(f"[DESCRIBE]({base_path}/describe/)")
tool_names.append(f"[REVIEW]({base_path}/review/)")
tool_names.append(f"[IMPROVE]({base_path}/improve/)")
tool_names.append(f"[UPDATE CHANGELOG]({base_path}/update_changelog/)")
tool_names.append(f"[ADD DOCS]({base_path}/documentation/) 💎")
tool_names.append(f"[TEST]({base_path}/test/) 💎")
tool_names.append(f"[IMPROVE COMPONENT]({base_path}/improve_component/) 💎")
tool_names.append(f"[ANALYZE]({base_path}/analyze/) 💎")
tool_names.append(f"[ASK]({base_path}/ask/)")
tool_names.append(f"[GENERATE CUSTOM LABELS]({base_path}/custom_labels/) 💎")
tool_names.append(f"[CI FEEDBACK]({base_path}/ci_feedback/) 💎")
tool_names.append(f"[CUSTOM PROMPT]({base_path}/custom_prompt/) 💎")
tool_names.append(f"[SIMILAR ISSUE]({base_path}/similar_issues/)")
descriptions = []
descriptions.append("Generates PR description - title, type, summary, code walkthrough and labels")
descriptions.append("Adjustable feedback about the PR, possible issues, security concerns, review effort and more")
descriptions.append("Code suggestions for improving the PR")
descriptions.append("Automatically updates the changelog")
descriptions.append("Generates documentation to methods/functions/classes that changed in the PR")
descriptions.append("Generates unit tests for a specific component, based on the PR code change")
descriptions.append("Code suggestions for a specific component that changed in the PR")
descriptions.append("Identifies code components that changed in the PR, and enables to interactively generate tests, docs, and code suggestions for each component")
descriptions.append("Answering free-text questions about the PR")
descriptions.append("Generates custom labels for the PR, based on specific guidelines defined by the user")
descriptions.append("Generates feedback and analysis for a failed CI job")
descriptions.append("Generates custom suggestions for improving the PR code, derived only from a specific guidelines prompt defined by the user")
descriptions.append("Automatically retrieves and presents similar issues")
commands =[]
commands.append("`/describe`")
commands.append("`/review`")
commands.append("`/improve`")
commands.append("`/update_changelog`")
commands.append("`/add_docs`")
commands.append("`/test`")
commands.append("`/improve_component`")
commands.append("`/analyze`")
commands.append("`/ask`")
commands.append("`/generate_labels`")
commands.append("`/checks`")
commands.append("`/custom_prompt`")
commands.append("`/similar_issue`")
checkbox_list = []
checkbox_list.append(" - [ ] Run <!-- /describe -->")
checkbox_list.append(" - [ ] Run <!-- /review -->")
checkbox_list.append(" - [ ] Run <!-- /improve -->")
checkbox_list.append(" - [ ] Run <!-- /update_changelog -->")
checkbox_list.append(" - [ ] Run <!-- /add_docs -->")
checkbox_list.append(" - [ ] Run <!-- /test -->")
checkbox_list.append(" - [ ] Run <!-- /improve_component -->")
checkbox_list.append(" - [ ] Run <!-- /analyze -->")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
checkbox_list.append("[*]")
if isinstance(self.git_provider, GithubProvider) and not get_settings().config.get('disable_checkboxes', False):
pr_comment += f"<table><tr align='left'><th align='left'>Tool</th><th align='left'>Description</th><th align='left'>Trigger Interactively :gem:</th></tr>"
for i in range(len(tool_names)):
pr_comment += f"\n<tr><td align='left'>\n\n<strong>{tool_names[i]}</strong></td>\n<td>{descriptions[i]}</td>\n<td>\n\n{checkbox_list[i]}\n</td></tr>"
pr_comment += "</table>\n\n"
pr_comment += f"""\n\n(1) Note that each tool be [triggered automatically](https://pr-agent-docs.codium.ai/usage-guide/automations_and_usage/#github-app-automatic-tools-when-a-new-pr-is-opened) when a new PR is opened, or called manually by [commenting on a PR](https://pr-agent-docs.codium.ai/usage-guide/automations_and_usage/#online-usage)."""
pr_comment += f"""\n\n(2) Tools marked with [*] require additional parameters to be passed. For example, to invoke the `/ask` tool, you need to comment on a PR: `/ask "<question content>"`. See the relevant documentation for each tool for more details."""
elif isinstance(self.git_provider, BitbucketServerProvider):
# only support basic commands in BBDC
pr_comment = generate_bbdc_table(tool_names[:4], descriptions[:4])
else:
pr_comment += f"<table><tr align='left'><th align='left'>Tool</th><th align='left'>Command</th><th align='left'>Description</th></tr>"
for i in range(len(tool_names)):
pr_comment += f"\n<tr><td align='left'>\n\n<strong>{tool_names[i]}</strong></td><td>{commands[i]}</td><td>{descriptions[i]}</td></tr>"
pr_comment += "</table>\n\n"
pr_comment += f"""\n\nNote that each tool be [invoked automatically](https://pr-agent-docs.codium.ai/usage-guide/automations_and_usage/) when a new PR is opened, or called manually by [commenting on a PR](https://pr-agent-docs.codium.ai/usage-guide/automations_and_usage/#online-usage)."""
if get_settings().config.publish_output:
self.git_provider.publish_comment(pr_comment)
except Exception as e:
get_logger().error(f"Error while running PRHelpMessage: {e}")
return ""
get_logger().exception(f"Error while running PRHelpMessage: {e}")
return ""
async def prepare_relevant_snippets(self, sim_results):
# Get relevant snippets
relevant_snippets_full = []
relevant_pages_full = []
relevant_snippets_full_header = []
th = 0.75
for s in sim_results:
page = s[0].metadata['source']
content = s[0].page_content
score = s[1]
relevant_snippets_full.append(content)
relevant_snippets_full_header.append(extract_header(content))
relevant_pages_full.append(page)
# build the snippets string
relevant_snippets_str = ""
for i, s in enumerate(relevant_snippets_full):
relevant_snippets_str += f"Snippet {i+1}:\n\n{s}\n\n"
relevant_snippets_str += "-------------------\n\n"
return relevant_pages_full, relevant_snippets_full_header, relevant_snippets_str
def generate_bbdc_table(column_arr_1, column_arr_2):
# Generating header row
header_row = "| Tool | Description | \n"
# Generating separator row
separator_row = "|--|--|\n"
# Generating data rows
data_rows = ""
max_len = max(len(column_arr_1), len(column_arr_2))
for i in range(max_len):
col1 = column_arr_1[i] if i < len(column_arr_1) else ""
col2 = column_arr_2[i] if i < len(column_arr_2) else ""
data_rows += f"| {col1} | {col2} |\n"
# Combine all parts to form the complete table
markdown_table = header_row + separator_row + data_rows
return markdown_table

View File

@ -6,7 +6,7 @@ from typing import List, Tuple
from jinja2 import Environment, StrictUndefined
from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler
from pr_agent.algo.ai_handlers.litellm_ai_handler import LiteLLMAIHandler
from pr_agent.algo.pr_processing import get_pr_diff, retry_with_fallback_models
from pr_agent.algo.pr_processing import get_pr_diff, retry_with_fallback_models, add_ai_metadata_to_diff_files
from pr_agent.algo.token_handler import TokenHandler
from pr_agent.algo.utils import github_action_output, load_yaml, ModelType, \
show_relevant_configurations, convert_to_markdown_v2, PRReviewHeader
@ -51,15 +51,23 @@ class PRReviewer:
raise Exception(f"Answer mode is not supported for {get_settings().config.git_provider} for now")
self.ai_handler = ai_handler()
self.ai_handler.main_pr_language = self.main_language
self.patches_diff = None
self.prediction = None
answer_str, question_str = self._get_user_answers()
self.pr_description, self.pr_description_files = (
self.git_provider.get_pr_description(split_changes_walkthrough=True))
if (self.pr_description_files and get_settings().get("config.is_auto_command", False) and
get_settings().get("config.enable_ai_metadata", False)):
add_ai_metadata_to_diff_files(self.git_provider, self.pr_description_files)
get_logger().debug(f"AI metadata added to the this command")
else:
get_settings().set("config.enable_ai_metadata", False)
get_logger().debug(f"AI metadata is disabled for this command")
self.vars = {
"title": self.git_provider.pr.title,
"branch": self.git_provider.get_pr_branch(),
"description": self.git_provider.get_pr_description(),
"description": self.pr_description,
"language": self.main_language,
"diff": "", # empty diff for initial calculation
"num_pr_files": self.git_provider.get_num_of_files(),
@ -75,7 +83,7 @@ class PRReviewer:
"commit_messages_str": self.git_provider.get_commit_messages(),
"custom_labels": "",
"enable_custom_labels": get_settings().config.enable_custom_labels,
"extra_issue_links": get_settings().pr_reviewer.extra_issue_links,
"is_ai_metadata": get_settings().get("config.enable_ai_metadata", False),
}
self.token_handler = TokenHandler(
@ -156,7 +164,7 @@ class PRReviewer:
self.token_handler,
model,
add_line_numbers_to_hunks=True,
disable_extra_lines=True,)
disable_extra_lines=False,)
if self.patches_diff:
get_logger().debug(f"PR diff", diff=self.patches_diff)

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "pr-agent"
version = "0.2.2"
version = "0.2.4"
authors = [{name= "CodiumAI", email = "tal.r@codium.ai"}]

View File

@ -1,4 +1,4 @@
aiohttp==3.9.4
aiohttp==3.9.5
anthropic[vertex]==0.21.3
atlassian-python-api==3.41.4
azure-devops==7.1.0b3
@ -13,7 +13,7 @@ Jinja2==3.1.2
litellm==1.43.13
loguru==0.7.2
msrest==0.7.1
openai==1.40.6
openai==1.46.0
pytest==7.4.0
PyGithub==1.59.*
PyYAML==6.0.1
@ -27,6 +27,19 @@ tenacity==8.2.3
gunicorn==22.0.0
pytest-cov==5.0.0
pydantic==2.8.2
html2text==2024.2.26
# help bot
langchain==0.3.0
langchain-openai==0.2.0
langchain-pinecone==0.2.0
langchain-chroma==0.1.4
chromadb==0.5.7
#
tree_sitter==0.21.3
tree_sitter_languages==1.10.2
grep_ast==0.3.3
#Pygments==2.18.0
# Uncomment the following lines to enable the 'similar issue' tool
# pinecone-client
# pinecone-datasets @ git+https://github.com/mrT23/pinecone-datasets.git@main

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