xai-review 0.25.0__tar.gz → 0.27.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of xai-review might be problematic. Click here for more details.

Files changed (287) hide show
  1. {xai_review-0.25.0 → xai_review-0.27.0}/PKG-INFO +4 -4
  2. {xai_review-0.25.0 → xai_review-0.27.0}/README.md +3 -3
  3. xai_review-0.27.0/ai_review/clients/bitbucket/client.py +31 -0
  4. xai_review-0.27.0/ai_review/clients/bitbucket/pr/client.py +141 -0
  5. xai_review-0.27.0/ai_review/clients/bitbucket/pr/schema/comments.py +49 -0
  6. xai_review-0.27.0/ai_review/clients/bitbucket/pr/schema/files.py +30 -0
  7. xai_review-0.27.0/ai_review/clients/bitbucket/pr/schema/pull_request.py +38 -0
  8. xai_review-0.27.0/ai_review/clients/bitbucket/pr/types.py +44 -0
  9. xai_review-0.27.0/ai_review/clients/bitbucket/tools.py +6 -0
  10. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/github/pr/client.py +66 -12
  11. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/github/pr/schema/comments.py +2 -1
  12. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/github/pr/schema/files.py +2 -1
  13. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/github/pr/schema/reviews.py +2 -1
  14. xai_review-0.27.0/ai_review/clients/github/tools.py +6 -0
  15. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/gitlab/mr/client.py +35 -6
  16. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/gitlab/mr/schema/discussions.py +2 -1
  17. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/gitlab/mr/schema/notes.py +2 -1
  18. xai_review-0.27.0/ai_review/clients/gitlab/tools.py +5 -0
  19. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/vcs/base.py +13 -1
  20. xai_review-0.27.0/ai_review/libs/config/vcs/bitbucket.py +13 -0
  21. xai_review-0.27.0/ai_review/libs/config/vcs/pagination.py +6 -0
  22. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/constants/vcs_provider.py +1 -0
  23. xai_review-0.27.0/ai_review/libs/http/paginate.py +43 -0
  24. xai_review-0.27.0/ai_review/services/vcs/bitbucket/client.py +185 -0
  25. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/vcs/factory.py +3 -0
  26. xai_review-0.27.0/ai_review/tests/fixtures/clients/bitbucket.py +204 -0
  27. xai_review-0.27.0/ai_review/tests/suites/clients/bitbucket/test_client.py +14 -0
  28. xai_review-0.27.0/ai_review/tests/suites/clients/bitbucket/test_tools.py +31 -0
  29. xai_review-0.27.0/ai_review/tests/suites/clients/github/test_tools.py +31 -0
  30. xai_review-0.27.0/ai_review/tests/suites/clients/gitlab/test_tools.py +26 -0
  31. xai_review-0.27.0/ai_review/tests/suites/libs/http/test_paginate.py +95 -0
  32. xai_review-0.27.0/ai_review/tests/suites/services/review/inline/__init__.py +0 -0
  33. xai_review-0.27.0/ai_review/tests/suites/services/review/policy/__init__.py +0 -0
  34. xai_review-0.27.0/ai_review/tests/suites/services/review/summary/__init__.py +0 -0
  35. xai_review-0.27.0/ai_review/tests/suites/services/vcs/__init__.py +0 -0
  36. xai_review-0.27.0/ai_review/tests/suites/services/vcs/bitbucket/__init__.py +0 -0
  37. xai_review-0.27.0/ai_review/tests/suites/services/vcs/bitbucket/test_service.py +117 -0
  38. xai_review-0.27.0/ai_review/tests/suites/services/vcs/github/__init__.py +0 -0
  39. xai_review-0.27.0/ai_review/tests/suites/services/vcs/gitlab/__init__.py +0 -0
  40. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/vcs/test_factory.py +8 -1
  41. {xai_review-0.25.0 → xai_review-0.27.0}/pyproject.toml +1 -1
  42. {xai_review-0.25.0 → xai_review-0.27.0}/xai_review.egg-info/PKG-INFO +4 -4
  43. {xai_review-0.25.0 → xai_review-0.27.0}/xai_review.egg-info/SOURCES.txt +27 -0
  44. {xai_review-0.25.0 → xai_review-0.27.0}/LICENSE +0 -0
  45. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/__init__.py +0 -0
  46. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/cli/__init__.py +0 -0
  47. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/cli/commands/__init__.py +0 -0
  48. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/cli/commands/run_context_review.py +0 -0
  49. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/cli/commands/run_inline_review.py +0 -0
  50. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/cli/commands/run_review.py +0 -0
  51. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/cli/commands/run_summary_review.py +0 -0
  52. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/cli/main.py +0 -0
  53. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/__init__.py +0 -0
  54. {xai_review-0.25.0/ai_review/clients/claude → xai_review-0.27.0/ai_review/clients/bitbucket}/__init__.py +0 -0
  55. {xai_review-0.25.0/ai_review/clients/gemini → xai_review-0.27.0/ai_review/clients/bitbucket/pr}/__init__.py +0 -0
  56. {xai_review-0.25.0/ai_review/clients/github → xai_review-0.27.0/ai_review/clients/bitbucket/pr/schema}/__init__.py +0 -0
  57. {xai_review-0.25.0/ai_review/clients/github/pr → xai_review-0.27.0/ai_review/clients/claude}/__init__.py +0 -0
  58. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/claude/client.py +0 -0
  59. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/claude/schema.py +0 -0
  60. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/claude/types.py +0 -0
  61. {xai_review-0.25.0/ai_review/clients/github/pr/schema → xai_review-0.27.0/ai_review/clients/gemini}/__init__.py +0 -0
  62. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/gemini/client.py +0 -0
  63. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/gemini/schema.py +0 -0
  64. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/gemini/types.py +0 -0
  65. {xai_review-0.25.0/ai_review/clients/gitlab → xai_review-0.27.0/ai_review/clients/github}/__init__.py +0 -0
  66. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/github/client.py +0 -0
  67. {xai_review-0.25.0/ai_review/clients/gitlab/mr → xai_review-0.27.0/ai_review/clients/github/pr}/__init__.py +0 -0
  68. {xai_review-0.25.0/ai_review/clients/gitlab/mr → xai_review-0.27.0/ai_review/clients/github/pr}/schema/__init__.py +0 -0
  69. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/github/pr/schema/pull_request.py +0 -0
  70. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/github/pr/types.py +0 -0
  71. {xai_review-0.25.0/ai_review/clients/ollama → xai_review-0.27.0/ai_review/clients/gitlab}/__init__.py +0 -0
  72. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/gitlab/client.py +0 -0
  73. {xai_review-0.25.0/ai_review/clients/openai → xai_review-0.27.0/ai_review/clients/gitlab/mr}/__init__.py +0 -0
  74. {xai_review-0.25.0/ai_review/libs → xai_review-0.27.0/ai_review/clients/gitlab/mr/schema}/__init__.py +0 -0
  75. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/gitlab/mr/schema/changes.py +0 -0
  76. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/gitlab/mr/types.py +0 -0
  77. {xai_review-0.25.0/ai_review/libs/asynchronous → xai_review-0.27.0/ai_review/clients/ollama}/__init__.py +0 -0
  78. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/ollama/client.py +0 -0
  79. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/ollama/schema.py +0 -0
  80. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/ollama/types.py +0 -0
  81. {xai_review-0.25.0/ai_review/libs/config → xai_review-0.27.0/ai_review/clients/openai}/__init__.py +0 -0
  82. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/openai/client.py +0 -0
  83. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/openai/schema.py +0 -0
  84. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/clients/openai/types.py +0 -0
  85. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/config.py +0 -0
  86. {xai_review-0.25.0/ai_review/libs/config/llm → xai_review-0.27.0/ai_review/libs}/__init__.py +0 -0
  87. {xai_review-0.25.0/ai_review/libs/config/vcs → xai_review-0.27.0/ai_review/libs/asynchronous}/__init__.py +0 -0
  88. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/asynchronous/gather.py +0 -0
  89. {xai_review-0.25.0/ai_review/libs/constants → xai_review-0.27.0/ai_review/libs/config}/__init__.py +0 -0
  90. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/artifacts.py +0 -0
  91. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/base.py +0 -0
  92. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/core.py +0 -0
  93. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/http.py +0 -0
  94. {xai_review-0.25.0/ai_review/libs/diff → xai_review-0.27.0/ai_review/libs/config/llm}/__init__.py +0 -0
  95. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/llm/base.py +0 -0
  96. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/llm/claude.py +0 -0
  97. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/llm/gemini.py +0 -0
  98. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/llm/meta.py +0 -0
  99. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/llm/ollama.py +0 -0
  100. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/llm/openai.py +0 -0
  101. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/logger.py +0 -0
  102. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/prompt.py +0 -0
  103. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/review.py +0 -0
  104. {xai_review-0.25.0/ai_review/libs/http → xai_review-0.27.0/ai_review/libs/config/vcs}/__init__.py +0 -0
  105. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/vcs/github.py +0 -0
  106. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/config/vcs/gitlab.py +0 -0
  107. {xai_review-0.25.0/ai_review/libs/http/event_hooks → xai_review-0.27.0/ai_review/libs/constants}/__init__.py +0 -0
  108. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/constants/llm_provider.py +0 -0
  109. {xai_review-0.25.0/ai_review/libs/http/transports → xai_review-0.27.0/ai_review/libs/diff}/__init__.py +0 -0
  110. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/diff/models.py +0 -0
  111. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/diff/parser.py +0 -0
  112. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/diff/tools.py +0 -0
  113. {xai_review-0.25.0/ai_review/libs/template → xai_review-0.27.0/ai_review/libs/http}/__init__.py +0 -0
  114. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/http/client.py +0 -0
  115. {xai_review-0.25.0/ai_review/prompts → xai_review-0.27.0/ai_review/libs/http/event_hooks}/__init__.py +0 -0
  116. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/http/event_hooks/base.py +0 -0
  117. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/http/event_hooks/logger.py +0 -0
  118. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/http/handlers.py +0 -0
  119. {xai_review-0.25.0/ai_review/resources → xai_review-0.27.0/ai_review/libs/http/transports}/__init__.py +0 -0
  120. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/http/transports/retry.py +0 -0
  121. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/json.py +0 -0
  122. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/logger.py +0 -0
  123. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/resources.py +0 -0
  124. {xai_review-0.25.0/ai_review/services → xai_review-0.27.0/ai_review/libs/template}/__init__.py +0 -0
  125. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/libs/template/render.py +0 -0
  126. {xai_review-0.25.0/ai_review/services/artifacts → xai_review-0.27.0/ai_review/prompts}/__init__.py +0 -0
  127. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/prompts/default_context.md +0 -0
  128. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/prompts/default_inline.md +0 -0
  129. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/prompts/default_summary.md +0 -0
  130. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/prompts/default_system_context.md +0 -0
  131. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/prompts/default_system_inline.md +0 -0
  132. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/prompts/default_system_summary.md +0 -0
  133. {xai_review-0.25.0/ai_review/services/cost → xai_review-0.27.0/ai_review/resources}/__init__.py +0 -0
  134. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/resources/pricing.yaml +0 -0
  135. {xai_review-0.25.0/ai_review/services/diff → xai_review-0.27.0/ai_review/services}/__init__.py +0 -0
  136. {xai_review-0.25.0/ai_review/services/git → xai_review-0.27.0/ai_review/services/artifacts}/__init__.py +0 -0
  137. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/artifacts/schema.py +0 -0
  138. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/artifacts/service.py +0 -0
  139. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/artifacts/tools.py +0 -0
  140. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/artifacts/types.py +0 -0
  141. {xai_review-0.25.0/ai_review/services/llm → xai_review-0.27.0/ai_review/services/cost}/__init__.py +0 -0
  142. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/cost/schema.py +0 -0
  143. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/cost/service.py +0 -0
  144. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/cost/types.py +0 -0
  145. {xai_review-0.25.0/ai_review/services/llm/claude → xai_review-0.27.0/ai_review/services/diff}/__init__.py +0 -0
  146. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/diff/renderers.py +0 -0
  147. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/diff/schema.py +0 -0
  148. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/diff/service.py +0 -0
  149. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/diff/tools.py +0 -0
  150. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/diff/types.py +0 -0
  151. {xai_review-0.25.0/ai_review/services/llm/gemini → xai_review-0.27.0/ai_review/services/git}/__init__.py +0 -0
  152. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/git/service.py +0 -0
  153. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/git/types.py +0 -0
  154. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/hook/__init__.py +0 -0
  155. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/hook/constants.py +0 -0
  156. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/hook/service.py +0 -0
  157. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/hook/types.py +0 -0
  158. {xai_review-0.25.0/ai_review/services/llm/ollama → xai_review-0.27.0/ai_review/services/llm}/__init__.py +0 -0
  159. {xai_review-0.25.0/ai_review/services/llm/openai → xai_review-0.27.0/ai_review/services/llm/claude}/__init__.py +0 -0
  160. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/llm/claude/client.py +0 -0
  161. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/llm/factory.py +0 -0
  162. {xai_review-0.25.0/ai_review/services/prompt → xai_review-0.27.0/ai_review/services/llm/gemini}/__init__.py +0 -0
  163. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/llm/gemini/client.py +0 -0
  164. {xai_review-0.25.0/ai_review/services/review → xai_review-0.27.0/ai_review/services/llm/ollama}/__init__.py +0 -0
  165. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/llm/ollama/client.py +0 -0
  166. {xai_review-0.25.0/ai_review/services/review/gateway → xai_review-0.27.0/ai_review/services/llm/openai}/__init__.py +0 -0
  167. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/llm/openai/client.py +0 -0
  168. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/llm/types.py +0 -0
  169. {xai_review-0.25.0/ai_review/services/review/inline → xai_review-0.27.0/ai_review/services/prompt}/__init__.py +0 -0
  170. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/prompt/adapter.py +0 -0
  171. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/prompt/schema.py +0 -0
  172. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/prompt/service.py +0 -0
  173. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/prompt/tools.py +0 -0
  174. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/prompt/types.py +0 -0
  175. {xai_review-0.25.0/ai_review/services/review/policy → xai_review-0.27.0/ai_review/services/review}/__init__.py +0 -0
  176. {xai_review-0.25.0/ai_review/services/review/summary → xai_review-0.27.0/ai_review/services/review/gateway}/__init__.py +0 -0
  177. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/gateway/comment.py +0 -0
  178. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/gateway/llm.py +0 -0
  179. {xai_review-0.25.0/ai_review/services/vcs → xai_review-0.27.0/ai_review/services/review/inline}/__init__.py +0 -0
  180. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/inline/schema.py +0 -0
  181. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/inline/service.py +0 -0
  182. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/inline/types.py +0 -0
  183. {xai_review-0.25.0/ai_review/services/vcs/github → xai_review-0.27.0/ai_review/services/review/policy}/__init__.py +0 -0
  184. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/policy/service.py +0 -0
  185. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/service.py +0 -0
  186. {xai_review-0.25.0/ai_review/services/vcs/gitlab → xai_review-0.27.0/ai_review/services/review/summary}/__init__.py +0 -0
  187. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/summary/schema.py +0 -0
  188. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/summary/service.py +0 -0
  189. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/review/summary/types.py +0 -0
  190. {xai_review-0.25.0/ai_review/tests → xai_review-0.27.0/ai_review/services/vcs}/__init__.py +0 -0
  191. {xai_review-0.25.0/ai_review/tests/fixtures → xai_review-0.27.0/ai_review/services/vcs/bitbucket}/__init__.py +0 -0
  192. {xai_review-0.25.0/ai_review/tests/fixtures/clients → xai_review-0.27.0/ai_review/services/vcs/github}/__init__.py +0 -0
  193. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/vcs/github/client.py +0 -0
  194. {xai_review-0.25.0/ai_review/tests/fixtures/services → xai_review-0.27.0/ai_review/services/vcs/gitlab}/__init__.py +0 -0
  195. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/vcs/gitlab/client.py +0 -0
  196. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/services/vcs/types.py +0 -0
  197. {xai_review-0.25.0/ai_review/tests/fixtures/services/review → xai_review-0.27.0/ai_review/tests}/__init__.py +0 -0
  198. {xai_review-0.25.0/ai_review/tests/suites → xai_review-0.27.0/ai_review/tests/fixtures}/__init__.py +0 -0
  199. {xai_review-0.25.0/ai_review/tests/suites → xai_review-0.27.0/ai_review/tests/fixtures}/clients/__init__.py +0 -0
  200. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/clients/claude.py +0 -0
  201. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/clients/gemini.py +0 -0
  202. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/clients/github.py +0 -0
  203. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/clients/gitlab.py +0 -0
  204. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/clients/ollama.py +0 -0
  205. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/clients/openai.py +0 -0
  206. {xai_review-0.25.0/ai_review/tests/suites/clients/claude → xai_review-0.27.0/ai_review/tests/fixtures/services}/__init__.py +0 -0
  207. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/services/artifacts.py +0 -0
  208. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/services/cost.py +0 -0
  209. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/services/diff.py +0 -0
  210. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/services/git.py +0 -0
  211. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/services/llm.py +0 -0
  212. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/services/prompt.py +0 -0
  213. {xai_review-0.25.0/ai_review/tests/suites/clients/gemini → xai_review-0.27.0/ai_review/tests/fixtures/services/review}/__init__.py +0 -0
  214. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/services/review/inline.py +0 -0
  215. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/services/review/summary.py +0 -0
  216. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/fixtures/services/vcs.py +0 -0
  217. {xai_review-0.25.0/ai_review/tests/suites/clients/github → xai_review-0.27.0/ai_review/tests/suites}/__init__.py +0 -0
  218. {xai_review-0.25.0/ai_review/tests/suites/clients/gitlab → xai_review-0.27.0/ai_review/tests/suites/clients}/__init__.py +0 -0
  219. {xai_review-0.25.0/ai_review/tests/suites/clients/ollama → xai_review-0.27.0/ai_review/tests/suites/clients/bitbucket}/__init__.py +0 -0
  220. {xai_review-0.25.0/ai_review/tests/suites/clients/openai → xai_review-0.27.0/ai_review/tests/suites/clients/claude}/__init__.py +0 -0
  221. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/claude/test_client.py +0 -0
  222. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/claude/test_schema.py +0 -0
  223. {xai_review-0.25.0/ai_review/tests/suites/libs → xai_review-0.27.0/ai_review/tests/suites/clients/gemini}/__init__.py +0 -0
  224. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/gemini/test_client.py +0 -0
  225. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/gemini/test_schema.py +0 -0
  226. {xai_review-0.25.0/ai_review/tests/suites/libs/asynchronous → xai_review-0.27.0/ai_review/tests/suites/clients/github}/__init__.py +0 -0
  227. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/github/test_client.py +0 -0
  228. {xai_review-0.25.0/ai_review/tests/suites/libs/config → xai_review-0.27.0/ai_review/tests/suites/clients/gitlab}/__init__.py +0 -0
  229. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/gitlab/test_client.py +0 -0
  230. {xai_review-0.25.0/ai_review/tests/suites/libs/diff → xai_review-0.27.0/ai_review/tests/suites/clients/ollama}/__init__.py +0 -0
  231. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/ollama/test_client.py +0 -0
  232. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/ollama/test_schema.py +0 -0
  233. {xai_review-0.25.0/ai_review/tests/suites/libs/template → xai_review-0.27.0/ai_review/tests/suites/clients/openai}/__init__.py +0 -0
  234. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/openai/test_client.py +0 -0
  235. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/clients/openai/test_schema.py +0 -0
  236. {xai_review-0.25.0/ai_review/tests/suites/services → xai_review-0.27.0/ai_review/tests/suites/libs}/__init__.py +0 -0
  237. {xai_review-0.25.0/ai_review/tests/suites/services/cost → xai_review-0.27.0/ai_review/tests/suites/libs/asynchronous}/__init__.py +0 -0
  238. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/libs/asynchronous/test_gather.py +0 -0
  239. {xai_review-0.25.0/ai_review/tests/suites/services/diff → xai_review-0.27.0/ai_review/tests/suites/libs/config}/__init__.py +0 -0
  240. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/libs/config/test_prompt.py +0 -0
  241. {xai_review-0.25.0/ai_review/tests/suites/services/hook → xai_review-0.27.0/ai_review/tests/suites/libs/diff}/__init__.py +0 -0
  242. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/libs/diff/test_models.py +0 -0
  243. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/libs/diff/test_parser.py +0 -0
  244. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/libs/diff/test_tools.py +0 -0
  245. {xai_review-0.25.0/ai_review/tests/suites/services/llm → xai_review-0.27.0/ai_review/tests/suites/libs/http}/__init__.py +0 -0
  246. {xai_review-0.25.0/ai_review/tests/suites/services/llm/claude → xai_review-0.27.0/ai_review/tests/suites/libs/template}/__init__.py +0 -0
  247. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/libs/template/test_render.py +0 -0
  248. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/libs/test_json.py +0 -0
  249. {xai_review-0.25.0/ai_review/tests/suites/services/llm/gemini → xai_review-0.27.0/ai_review/tests/suites/services}/__init__.py +0 -0
  250. {xai_review-0.25.0/ai_review/tests/suites/services/llm/ollama → xai_review-0.27.0/ai_review/tests/suites/services/cost}/__init__.py +0 -0
  251. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/cost/test_schema.py +0 -0
  252. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/cost/test_service.py +0 -0
  253. {xai_review-0.25.0/ai_review/tests/suites/services/llm/openai → xai_review-0.27.0/ai_review/tests/suites/services/diff}/__init__.py +0 -0
  254. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/diff/test_renderers.py +0 -0
  255. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/diff/test_service.py +0 -0
  256. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/diff/test_tools.py +0 -0
  257. {xai_review-0.25.0/ai_review/tests/suites/services/prompt → xai_review-0.27.0/ai_review/tests/suites/services/hook}/__init__.py +0 -0
  258. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/hook/test_service.py +0 -0
  259. {xai_review-0.25.0/ai_review/tests/suites/services/review → xai_review-0.27.0/ai_review/tests/suites/services/llm}/__init__.py +0 -0
  260. {xai_review-0.25.0/ai_review/tests/suites/services/review/inline → xai_review-0.27.0/ai_review/tests/suites/services/llm/claude}/__init__.py +0 -0
  261. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/llm/claude/test_client.py +0 -0
  262. {xai_review-0.25.0/ai_review/tests/suites/services/review/policy → xai_review-0.27.0/ai_review/tests/suites/services/llm/gemini}/__init__.py +0 -0
  263. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/llm/gemini/test_client.py +0 -0
  264. {xai_review-0.25.0/ai_review/tests/suites/services/review/summary → xai_review-0.27.0/ai_review/tests/suites/services/llm/ollama}/__init__.py +0 -0
  265. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/llm/ollama/test_client.py +0 -0
  266. {xai_review-0.25.0/ai_review/tests/suites/services/vcs → xai_review-0.27.0/ai_review/tests/suites/services/llm/openai}/__init__.py +0 -0
  267. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/llm/openai/test_client.py +0 -0
  268. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/llm/test_factory.py +0 -0
  269. {xai_review-0.25.0/ai_review/tests/suites/services/vcs/github → xai_review-0.27.0/ai_review/tests/suites/services/prompt}/__init__.py +0 -0
  270. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/prompt/test_adapter.py +0 -0
  271. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/prompt/test_schema.py +0 -0
  272. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/prompt/test_service.py +0 -0
  273. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/prompt/test_tools.py +0 -0
  274. {xai_review-0.25.0/ai_review/tests/suites/services/vcs/gitlab → xai_review-0.27.0/ai_review/tests/suites/services/review}/__init__.py +0 -0
  275. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/review/inline/test_schema.py +0 -0
  276. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/review/inline/test_service.py +0 -0
  277. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/review/policy/test_service.py +0 -0
  278. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/review/summary/test_schema.py +0 -0
  279. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/review/summary/test_service.py +0 -0
  280. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/review/test_service.py +0 -0
  281. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/vcs/github/test_service.py +0 -0
  282. {xai_review-0.25.0 → xai_review-0.27.0}/ai_review/tests/suites/services/vcs/gitlab/test_service.py +0 -0
  283. {xai_review-0.25.0 → xai_review-0.27.0}/setup.cfg +0 -0
  284. {xai_review-0.25.0 → xai_review-0.27.0}/xai_review.egg-info/dependency_links.txt +0 -0
  285. {xai_review-0.25.0 → xai_review-0.27.0}/xai_review.egg-info/entry_points.txt +0 -0
  286. {xai_review-0.25.0 → xai_review-0.27.0}/xai_review.egg-info/requires.txt +0 -0
  287. {xai_review-0.25.0 → xai_review-0.27.0}/xai_review.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xai-review
3
- Version: 0.25.0
3
+ Version: 0.27.0
4
4
  Summary: AI-powered code review tool
5
5
  Author-email: Nikita Filonov <nikita.filonov@example.com>
6
6
  Maintainer-email: Nikita Filonov <nikita.filonov@example.com>
@@ -67,7 +67,7 @@ improve code quality, enforce consistency, and speed up the review process.
67
67
  ✨ Key features:
68
68
 
69
69
  - **Multiple LLM providers** — choose between **OpenAI**, **Claude**, **Gemini**, or **Ollama**, and switch anytime.
70
- - **VCS integration** — works out of the box with GitLab, GitHub (more providers coming).
70
+ - **VCS integration** — works out of the box with **GitLab**, **GitHub**, and **Bitbucket**.
71
71
  - **Customizable prompts** — adapt inline, context, and summary reviews to match your team’s coding guidelines.
72
72
  - **Flexible configuration** — supports `YAML`, `JSON`, and `ENV`, with seamless overrides in CI/CD pipelines.
73
73
  - **AI Review runs fully client-side** — it never proxies or inspects your requests.
@@ -170,7 +170,7 @@ Key things you can customize:
170
170
 
171
171
  - **LLM provider** — OpenAI, Gemini, Claude, or Ollama
172
172
  - **Model settings** — model name, temperature, max tokens
173
- - **VCS integration** — works out of the box with **GitLab** and **GitHub**.
173
+ - **VCS integration** — works out of the box with **GitLab**, **GitHub**, and **Bitbucket**
174
174
  - **Review policy** — which files to include/exclude, review modes
175
175
  - **Prompts** — inline/context/summary prompt templates
176
176
 
@@ -209,7 +209,7 @@ jobs:
209
209
  runs-on: ubuntu-latest
210
210
  steps:
211
211
  - uses: actions/checkout@v4
212
- - uses: Nikita-Filonov/ai-review@v0.25.0
212
+ - uses: Nikita-Filonov/ai-review@v0.27.0
213
213
  with:
214
214
  review-command: ${{ inputs.review-command }}
215
215
  env:
@@ -33,7 +33,7 @@ improve code quality, enforce consistency, and speed up the review process.
33
33
  ✨ Key features:
34
34
 
35
35
  - **Multiple LLM providers** — choose between **OpenAI**, **Claude**, **Gemini**, or **Ollama**, and switch anytime.
36
- - **VCS integration** — works out of the box with GitLab, GitHub (more providers coming).
36
+ - **VCS integration** — works out of the box with **GitLab**, **GitHub**, and **Bitbucket**.
37
37
  - **Customizable prompts** — adapt inline, context, and summary reviews to match your team’s coding guidelines.
38
38
  - **Flexible configuration** — supports `YAML`, `JSON`, and `ENV`, with seamless overrides in CI/CD pipelines.
39
39
  - **AI Review runs fully client-side** — it never proxies or inspects your requests.
@@ -136,7 +136,7 @@ Key things you can customize:
136
136
 
137
137
  - **LLM provider** — OpenAI, Gemini, Claude, or Ollama
138
138
  - **Model settings** — model name, temperature, max tokens
139
- - **VCS integration** — works out of the box with **GitLab** and **GitHub**.
139
+ - **VCS integration** — works out of the box with **GitLab**, **GitHub**, and **Bitbucket**
140
140
  - **Review policy** — which files to include/exclude, review modes
141
141
  - **Prompts** — inline/context/summary prompt templates
142
142
 
@@ -175,7 +175,7 @@ jobs:
175
175
  runs-on: ubuntu-latest
176
176
  steps:
177
177
  - uses: actions/checkout@v4
178
- - uses: Nikita-Filonov/ai-review@v0.25.0
178
+ - uses: Nikita-Filonov/ai-review@v0.27.0
179
179
  with:
180
180
  review-command: ${{ inputs.review-command }}
181
181
  env:
@@ -0,0 +1,31 @@
1
+ from ai_review.clients.bitbucket.pr.client import BitbucketPullRequestsHTTPClient
2
+ from httpx import AsyncClient, AsyncHTTPTransport
3
+
4
+ from ai_review.config import settings
5
+ from ai_review.libs.http.event_hooks.logger import LoggerEventHook
6
+ from ai_review.libs.http.transports.retry import RetryTransport
7
+ from ai_review.libs.logger import get_logger
8
+
9
+
10
+ class BitbucketHTTPClient:
11
+ def __init__(self, client: AsyncClient):
12
+ self.pr = BitbucketPullRequestsHTTPClient(client)
13
+
14
+
15
+ def get_bitbucket_http_client() -> BitbucketHTTPClient:
16
+ logger = get_logger("BITBUCKET_HTTP_CLIENT")
17
+ logger_event_hook = LoggerEventHook(logger=logger)
18
+ retry_transport = RetryTransport(logger=logger, transport=AsyncHTTPTransport())
19
+
20
+ client = AsyncClient(
21
+ timeout=settings.llm.http_client.timeout,
22
+ headers={"Authorization": f"Bearer {settings.vcs.http_client.api_token_value}"},
23
+ base_url=settings.vcs.http_client.api_url_value,
24
+ transport=retry_transport,
25
+ event_hooks={
26
+ "request": [logger_event_hook.request],
27
+ "response": [logger_event_hook.response],
28
+ }
29
+ )
30
+
31
+ return BitbucketHTTPClient(client=client)
@@ -0,0 +1,141 @@
1
+ from httpx import Response, QueryParams
2
+
3
+ from ai_review.clients.bitbucket.pr.schema.comments import (
4
+ BitbucketPRCommentSchema,
5
+ BitbucketGetPRCommentsQuerySchema,
6
+ BitbucketGetPRCommentsResponseSchema,
7
+ BitbucketCreatePRCommentRequestSchema,
8
+ BitbucketCreatePRCommentResponseSchema,
9
+ )
10
+ from ai_review.clients.bitbucket.pr.schema.files import (
11
+ BitbucketPRFileSchema,
12
+ BitbucketGetPRFilesQuerySchema,
13
+ BitbucketGetPRFilesResponseSchema,
14
+ )
15
+ from ai_review.clients.bitbucket.pr.schema.pull_request import BitbucketGetPRResponseSchema
16
+ from ai_review.clients.bitbucket.pr.types import BitbucketPullRequestsHTTPClientProtocol
17
+ from ai_review.clients.bitbucket.tools import bitbucket_has_next_page
18
+ from ai_review.config import settings
19
+ from ai_review.libs.http.client import HTTPClient
20
+ from ai_review.libs.http.handlers import handle_http_error, HTTPClientError
21
+ from ai_review.libs.http.paginate import paginate
22
+
23
+
24
+ class BitbucketPullRequestsHTTPClientError(HTTPClientError):
25
+ pass
26
+
27
+
28
+ class BitbucketPullRequestsHTTPClient(HTTPClient, BitbucketPullRequestsHTTPClientProtocol):
29
+ @handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
30
+ async def get_pull_request_api(self, workspace: str, repo_slug: str, pull_request_id: str) -> Response:
31
+ return await self.get(f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}")
32
+
33
+ @handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
34
+ async def get_diffstat_api(
35
+ self,
36
+ workspace: str,
37
+ repo_slug: str,
38
+ pull_request_id: str,
39
+ query: BitbucketGetPRFilesQuerySchema,
40
+ ) -> Response:
41
+ return await self.get(
42
+ f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/diffstat",
43
+ query=QueryParams(**query.model_dump(by_alias=True)),
44
+ )
45
+
46
+ @handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
47
+ async def get_comments_api(
48
+ self,
49
+ workspace: str,
50
+ repo_slug: str,
51
+ pull_request_id: str,
52
+ query: BitbucketGetPRCommentsQuerySchema,
53
+ ) -> Response:
54
+ return await self.get(
55
+ f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/comments",
56
+ query=QueryParams(**query.model_dump(by_alias=True)),
57
+ )
58
+
59
+ @handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
60
+ async def create_comment_api(
61
+ self,
62
+ workspace: str,
63
+ repo_slug: str,
64
+ pull_request_id: str,
65
+ request: BitbucketCreatePRCommentRequestSchema,
66
+ ) -> Response:
67
+ return await self.post(
68
+ f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/comments",
69
+ json=request.model_dump(by_alias=True),
70
+ )
71
+
72
+ async def get_pull_request(
73
+ self,
74
+ workspace: str,
75
+ repo_slug: str,
76
+ pull_request_id: str
77
+ ) -> BitbucketGetPRResponseSchema:
78
+ resp = await self.get_pull_request_api(workspace, repo_slug, pull_request_id)
79
+ return BitbucketGetPRResponseSchema.model_validate_json(resp.text)
80
+
81
+ async def get_files(
82
+ self,
83
+ workspace: str,
84
+ repo_slug: str,
85
+ pull_request_id: str
86
+ ) -> BitbucketGetPRFilesResponseSchema:
87
+ async def fetch_page(page: int) -> Response:
88
+ query = BitbucketGetPRFilesQuerySchema(page=page, page_len=settings.vcs.pagination.per_page)
89
+ return await self.get_diffstat_api(workspace, repo_slug, pull_request_id, query)
90
+
91
+ def extract_items(response: Response) -> list[BitbucketPRFileSchema]:
92
+ result = BitbucketGetPRFilesResponseSchema.model_validate_json(response.text)
93
+ return result.values
94
+
95
+ items = await paginate(
96
+ max_pages=settings.vcs.pagination.max_pages,
97
+ fetch_page=fetch_page,
98
+ extract_items=extract_items,
99
+ has_next_page=bitbucket_has_next_page
100
+ )
101
+ return BitbucketGetPRFilesResponseSchema(
102
+ size=len(items),
103
+ values=items,
104
+ page_len=settings.vcs.pagination.per_page
105
+ )
106
+
107
+ async def get_comments(
108
+ self,
109
+ workspace: str,
110
+ repo_slug: str,
111
+ pull_request_id: str
112
+ ) -> BitbucketGetPRCommentsResponseSchema:
113
+ async def fetch_page(page: int) -> Response:
114
+ query = BitbucketGetPRCommentsQuerySchema(page=page, page_len=settings.vcs.pagination.per_page)
115
+ return await self.get_comments_api(workspace, repo_slug, pull_request_id, query)
116
+
117
+ def extract_items(response: Response) -> list[BitbucketPRCommentSchema]:
118
+ result = BitbucketGetPRCommentsResponseSchema.model_validate_json(response.text)
119
+ return result.values
120
+
121
+ items = await paginate(
122
+ max_pages=settings.vcs.pagination.max_pages,
123
+ fetch_page=fetch_page,
124
+ extract_items=extract_items,
125
+ has_next_page=bitbucket_has_next_page
126
+ )
127
+ return BitbucketGetPRCommentsResponseSchema(
128
+ size=len(items),
129
+ values=items,
130
+ page_len=settings.vcs.pagination.per_page
131
+ )
132
+
133
+ async def create_comment(
134
+ self,
135
+ workspace: str,
136
+ repo_slug: str,
137
+ pull_request_id: str,
138
+ request: BitbucketCreatePRCommentRequestSchema
139
+ ) -> BitbucketCreatePRCommentResponseSchema:
140
+ response = await self.create_comment_api(workspace, repo_slug, pull_request_id, request)
141
+ return BitbucketCreatePRCommentResponseSchema.model_validate_json(response.text)
@@ -0,0 +1,49 @@
1
+ from pydantic import BaseModel, Field, ConfigDict
2
+
3
+
4
+ class BitbucketCommentContentSchema(BaseModel):
5
+ raw: str
6
+ html: str | None = None
7
+ markup: str | None = None
8
+
9
+
10
+ class BitbucketCommentInlineSchema(BaseModel):
11
+ model_config = ConfigDict(populate_by_name=True)
12
+
13
+ path: str
14
+ to_line: int | None = Field(alias="to", default=None)
15
+ from_line: int | None = Field(alias="from", default=None)
16
+
17
+
18
+ class BitbucketPRCommentSchema(BaseModel):
19
+ id: int
20
+ inline: BitbucketCommentInlineSchema | None = None
21
+ content: BitbucketCommentContentSchema
22
+
23
+
24
+ class BitbucketGetPRCommentsQuerySchema(BaseModel):
25
+ model_config = ConfigDict(populate_by_name=True)
26
+
27
+ page: int = 1
28
+ page_len: int = Field(alias="pagelen", default=100)
29
+
30
+
31
+ class BitbucketGetPRCommentsResponseSchema(BaseModel):
32
+ model_config = ConfigDict(populate_by_name=True)
33
+
34
+ size: int
35
+ page: int | None = None
36
+ next: str | None = None
37
+ values: list[BitbucketPRCommentSchema]
38
+ page_len: int = Field(alias="pagelen")
39
+
40
+
41
+ class BitbucketCreatePRCommentRequestSchema(BaseModel):
42
+ inline: BitbucketCommentInlineSchema | None = None
43
+ content: BitbucketCommentContentSchema
44
+
45
+
46
+ class BitbucketCreatePRCommentResponseSchema(BaseModel):
47
+ id: int
48
+ inline: BitbucketCommentInlineSchema | None = None
49
+ content: BitbucketCommentContentSchema
@@ -0,0 +1,30 @@
1
+ from pydantic import BaseModel, Field, ConfigDict
2
+
3
+
4
+ class BitbucketPRFilePathSchema(BaseModel):
5
+ path: str
6
+
7
+
8
+ class BitbucketPRFileSchema(BaseModel):
9
+ new: BitbucketPRFilePathSchema | None = None
10
+ old: BitbucketPRFilePathSchema | None = None
11
+ status: str
12
+ lines_added: int
13
+ lines_removed: int
14
+
15
+
16
+ class BitbucketGetPRFilesQuerySchema(BaseModel):
17
+ model_config = ConfigDict(populate_by_name=True)
18
+
19
+ page: int = 1
20
+ page_len: int = Field(alias="pagelen", default=100)
21
+
22
+
23
+ class BitbucketGetPRFilesResponseSchema(BaseModel):
24
+ model_config = ConfigDict(populate_by_name=True)
25
+
26
+ size: int
27
+ page: int | None = None
28
+ next: str | None = None
29
+ values: list[BitbucketPRFileSchema]
30
+ page_len: int = Field(alias="pagelen")
@@ -0,0 +1,38 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ class BitbucketUserSchema(BaseModel):
5
+ uuid: str
6
+ nickname: str
7
+ display_name: str
8
+
9
+
10
+ class BitbucketBranchSchema(BaseModel):
11
+ name: str
12
+
13
+
14
+ class BitbucketCommitSchema(BaseModel):
15
+ hash: str
16
+
17
+
18
+ class BitbucketRepositorySchema(BaseModel):
19
+ uuid: str
20
+ full_name: str
21
+
22
+
23
+ class BitbucketPRLocationSchema(BaseModel):
24
+ branch: BitbucketBranchSchema
25
+ commit: BitbucketCommitSchema
26
+ repository: BitbucketRepositorySchema
27
+
28
+
29
+ class BitbucketGetPRResponseSchema(BaseModel):
30
+ id: int
31
+ title: str
32
+ description: str | None = None
33
+ state: str
34
+ author: BitbucketUserSchema
35
+ source: BitbucketPRLocationSchema
36
+ destination: BitbucketPRLocationSchema
37
+ reviewers: list[BitbucketUserSchema] = Field(default_factory=list)
38
+ participants: list[BitbucketUserSchema] = Field(default_factory=list)
@@ -0,0 +1,44 @@
1
+ from typing import Protocol
2
+
3
+ from ai_review.clients.bitbucket.pr.schema.comments import (
4
+ BitbucketGetPRCommentsResponseSchema,
5
+ BitbucketCreatePRCommentRequestSchema,
6
+ BitbucketCreatePRCommentResponseSchema,
7
+ )
8
+ from ai_review.clients.bitbucket.pr.schema.files import BitbucketGetPRFilesResponseSchema
9
+ from ai_review.clients.bitbucket.pr.schema.pull_request import BitbucketGetPRResponseSchema
10
+
11
+
12
+ class BitbucketPullRequestsHTTPClientProtocol(Protocol):
13
+ async def get_pull_request(
14
+ self,
15
+ workspace: str,
16
+ repo_slug: str,
17
+ pull_request_id: str
18
+ ) -> BitbucketGetPRResponseSchema:
19
+ ...
20
+
21
+ async def get_files(
22
+ self,
23
+ workspace: str,
24
+ repo_slug: str,
25
+ pull_request_id: str
26
+ ) -> BitbucketGetPRFilesResponseSchema:
27
+ ...
28
+
29
+ async def get_comments(
30
+ self,
31
+ workspace: str,
32
+ repo_slug: str,
33
+ pull_request_id: str
34
+ ) -> BitbucketGetPRCommentsResponseSchema:
35
+ ...
36
+
37
+ async def create_comment(
38
+ self,
39
+ workspace: str,
40
+ repo_slug: str,
41
+ pull_request_id: str,
42
+ request: BitbucketCreatePRCommentRequestSchema,
43
+ ) -> BitbucketCreatePRCommentResponseSchema:
44
+ ...
@@ -0,0 +1,6 @@
1
+ from httpx import Response
2
+
3
+
4
+ def bitbucket_has_next_page(response: Response) -> bool:
5
+ data = response.json()
6
+ return bool(data.get("next"))
@@ -1,6 +1,7 @@
1
1
  from httpx import Response, QueryParams
2
2
 
3
3
  from ai_review.clients.github.pr.schema.comments import (
4
+ GitHubPRCommentSchema,
4
5
  GitHubGetPRCommentsQuerySchema,
5
6
  GitHubGetPRCommentsResponseSchema,
6
7
  GitHubCreateIssueCommentRequestSchema,
@@ -9,17 +10,22 @@ from ai_review.clients.github.pr.schema.comments import (
9
10
  GitHubCreateReviewCommentResponseSchema
10
11
  )
11
12
  from ai_review.clients.github.pr.schema.files import (
13
+ GitHubPRFileSchema,
12
14
  GitHubGetPRFilesQuerySchema,
13
15
  GitHubGetPRFilesResponseSchema
14
16
  )
15
17
  from ai_review.clients.github.pr.schema.pull_request import GitHubGetPRResponseSchema
16
18
  from ai_review.clients.github.pr.schema.reviews import (
19
+ GitHubPRReviewSchema,
17
20
  GitHubGetPRReviewsQuerySchema,
18
21
  GitHubGetPRReviewsResponseSchema
19
22
  )
20
23
  from ai_review.clients.github.pr.types import GitHubPullRequestsHTTPClientProtocol
24
+ from ai_review.clients.github.tools import github_has_next_page
25
+ from ai_review.config import settings
21
26
  from ai_review.libs.http.client import HTTPClient
22
27
  from ai_review.libs.http.handlers import HTTPClientError, handle_http_error
28
+ from ai_review.libs.http.paginate import paginate
23
29
 
24
30
 
25
31
  class GitHubPullRequestsHTTPClientError(HTTPClientError):
@@ -114,24 +120,72 @@ class GitHubPullRequestsHTTPClient(HTTPClient, GitHubPullRequestsHTTPClientProto
114
120
  return GitHubGetPRResponseSchema.model_validate_json(response.text)
115
121
 
116
122
  async def get_files(self, owner: str, repo: str, pull_number: str) -> GitHubGetPRFilesResponseSchema:
117
- query = GitHubGetPRFilesQuerySchema(per_page=100)
118
- response = await self.get_files_api(owner, repo, pull_number, query)
119
- return GitHubGetPRFilesResponseSchema.model_validate_json(response.text)
123
+ async def fetch_page(page: int) -> Response:
124
+ query = GitHubGetPRFilesQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
125
+ return await self.get_files_api(owner, repo, pull_number, query)
126
+
127
+ def extract_items(response: Response) -> list[GitHubPRFileSchema]:
128
+ result = GitHubGetPRFilesResponseSchema.model_validate_json(response.text)
129
+ return result.root
130
+
131
+ items = await paginate(
132
+ max_pages=settings.vcs.pagination.max_pages,
133
+ fetch_page=fetch_page,
134
+ extract_items=extract_items,
135
+ has_next_page=github_has_next_page
136
+ )
137
+ return GitHubGetPRFilesResponseSchema(root=items)
120
138
 
121
139
  async def get_issue_comments(self, owner: str, repo: str, issue_number: str) -> GitHubGetPRCommentsResponseSchema:
122
- query = GitHubGetPRCommentsQuerySchema(per_page=100)
123
- response = await self.get_issue_comments_api(owner, repo, issue_number, query)
124
- return GitHubGetPRCommentsResponseSchema.model_validate_json(response.text)
140
+ async def fetch_page(page: int) -> Response:
141
+ query = GitHubGetPRCommentsQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
142
+ return await self.get_issue_comments_api(owner, repo, issue_number, query)
143
+
144
+ def extract_items(response: Response) -> list[GitHubPRCommentSchema]:
145
+ result = GitHubGetPRCommentsResponseSchema.model_validate_json(response.text)
146
+ return result.root
147
+
148
+ items = await paginate(
149
+ max_pages=settings.vcs.pagination.max_pages,
150
+ fetch_page=fetch_page,
151
+ extract_items=extract_items,
152
+ has_next_page=github_has_next_page
153
+ )
154
+ return GitHubGetPRCommentsResponseSchema(root=items)
125
155
 
126
156
  async def get_review_comments(self, owner: str, repo: str, pull_number: str) -> GitHubGetPRCommentsResponseSchema:
127
- query = GitHubGetPRCommentsQuerySchema(per_page=100)
128
- response = await self.get_review_comments_api(owner, repo, pull_number, query)
129
- return GitHubGetPRCommentsResponseSchema.model_validate_json(response.text)
157
+ async def fetch_page(page: int) -> Response:
158
+ query = GitHubGetPRCommentsQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
159
+ return await self.get_review_comments_api(owner, repo, pull_number, query)
160
+
161
+ def extract_items(response: Response) -> list[GitHubPRCommentSchema]:
162
+ result = GitHubGetPRCommentsResponseSchema.model_validate_json(response.text)
163
+ return result.root
164
+
165
+ items = await paginate(
166
+ max_pages=settings.vcs.pagination.max_pages,
167
+ fetch_page=fetch_page,
168
+ extract_items=extract_items,
169
+ has_next_page=github_has_next_page
170
+ )
171
+ return GitHubGetPRCommentsResponseSchema(root=items)
130
172
 
131
173
  async def get_reviews(self, owner: str, repo: str, pull_number: str) -> GitHubGetPRReviewsResponseSchema:
132
- query = GitHubGetPRReviewsQuerySchema(per_page=100)
133
- response = await self.get_reviews_api(owner, repo, pull_number, query)
134
- return GitHubGetPRReviewsResponseSchema.model_validate_json(response.text)
174
+ async def fetch_page(page: int) -> Response:
175
+ query = GitHubGetPRReviewsQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
176
+ return await self.get_reviews_api(owner, repo, pull_number, query)
177
+
178
+ def extract_items(response: Response) -> list[GitHubPRReviewSchema]:
179
+ result = GitHubGetPRReviewsResponseSchema.model_validate_json(response.text)
180
+ return result.root
181
+
182
+ items = await paginate(
183
+ max_pages=settings.vcs.pagination.max_pages,
184
+ fetch_page=fetch_page,
185
+ extract_items=extract_items,
186
+ has_next_page=github_has_next_page
187
+ )
188
+ return GitHubGetPRReviewsResponseSchema(root=items)
135
189
 
136
190
  async def create_review_comment(
137
191
  self,
@@ -9,7 +9,8 @@ class GitHubPRCommentSchema(BaseModel):
9
9
 
10
10
 
11
11
  class GitHubGetPRCommentsQuerySchema(BaseModel):
12
- per_page: int
12
+ page: int = 1
13
+ per_page: int = 100
13
14
 
14
15
 
15
16
  class GitHubGetPRCommentsResponseSchema(RootModel[list[GitHubPRCommentSchema]]):
@@ -9,7 +9,8 @@ class GitHubPRFileSchema(BaseModel):
9
9
 
10
10
 
11
11
  class GitHubGetPRFilesQuerySchema(BaseModel):
12
- per_page: int
12
+ page: int = 1
13
+ per_page: int = 100
13
14
 
14
15
 
15
16
  class GitHubGetPRFilesResponseSchema(RootModel[list[GitHubPRFileSchema]]):
@@ -10,7 +10,8 @@ class GitHubPRReviewSchema(BaseModel):
10
10
 
11
11
 
12
12
  class GitHubGetPRReviewsQuerySchema(BaseModel):
13
- per_page: int
13
+ page: int = 1
14
+ per_page: int = 100
14
15
 
15
16
 
16
17
  class GitHubGetPRReviewsResponseSchema(RootModel[list[GitHubPRReviewSchema]]):
@@ -0,0 +1,6 @@
1
+ from httpx import Response
2
+
3
+
4
+ def github_has_next_page(response: Response) -> bool:
5
+ link_header = response.headers.get("Link")
6
+ return (link_header is not None) and ('rel="next"' in link_header)
@@ -2,20 +2,25 @@ from httpx import Response, QueryParams
2
2
 
3
3
  from ai_review.clients.gitlab.mr.schema.changes import GitLabGetMRChangesResponseSchema
4
4
  from ai_review.clients.gitlab.mr.schema.discussions import (
5
+ GitLabDiscussionSchema,
5
6
  GitLabGetMRDiscussionsQuerySchema,
6
7
  GitLabGetMRDiscussionsResponseSchema,
7
8
  GitLabCreateMRDiscussionRequestSchema,
8
9
  GitLabCreateMRDiscussionResponseSchema
9
10
  )
10
11
  from ai_review.clients.gitlab.mr.schema.notes import (
12
+ GitLabNoteSchema,
11
13
  GitLabGetMRNotesQuerySchema,
12
14
  GitLabGetMRNotesResponseSchema,
13
15
  GitLabCreateMRNoteRequestSchema,
14
16
  GitLabCreateMRNoteResponseSchema,
15
17
  )
16
18
  from ai_review.clients.gitlab.mr.types import GitLabMergeRequestsHTTPClientProtocol
19
+ from ai_review.clients.gitlab.tools import gitlab_has_next_page
20
+ from ai_review.config import settings
17
21
  from ai_review.libs.http.client import HTTPClient
18
22
  from ai_review.libs.http.handlers import handle_http_error, HTTPClientError
23
+ from ai_review.libs.http.paginate import paginate
19
24
 
20
25
 
21
26
  class GitLabMergeRequestsHTTPClientError(HTTPClientError):
@@ -86,18 +91,42 @@ class GitLabMergeRequestsHTTPClient(HTTPClient, GitLabMergeRequestsHTTPClientPro
86
91
  project_id: str,
87
92
  merge_request_id: str
88
93
  ) -> GitLabGetMRNotesResponseSchema:
89
- query = GitLabGetMRNotesQuerySchema(per_page=100)
90
- response = await self.get_notes_api(project_id, merge_request_id, query)
91
- return GitLabGetMRNotesResponseSchema.model_validate_json(response.text)
94
+ async def fetch_page(page: int) -> Response:
95
+ query = GitLabGetMRNotesQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
96
+ return await self.get_notes_api(project_id, merge_request_id, query)
97
+
98
+ def extract_items(response: Response) -> list[GitLabNoteSchema]:
99
+ result = GitLabGetMRNotesResponseSchema.model_validate_json(response.text)
100
+ return result.root
101
+
102
+ items = await paginate(
103
+ max_pages=settings.vcs.pagination.max_pages,
104
+ fetch_page=fetch_page,
105
+ extract_items=extract_items,
106
+ has_next_page=gitlab_has_next_page
107
+ )
108
+ return GitLabGetMRNotesResponseSchema(root=items)
92
109
 
93
110
  async def get_discussions(
94
111
  self,
95
112
  project_id: str,
96
113
  merge_request_id: str
97
114
  ) -> GitLabGetMRDiscussionsResponseSchema:
98
- query = GitLabGetMRDiscussionsQuerySchema(per_page=100)
99
- response = await self.get_discussions_api(project_id, merge_request_id, query)
100
- return GitLabGetMRDiscussionsResponseSchema.model_validate_json(response.text)
115
+ async def fetch_page(page: int) -> Response:
116
+ query = GitLabGetMRDiscussionsQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
117
+ return await self.get_discussions_api(project_id, merge_request_id, query)
118
+
119
+ def extract_items(response: Response) -> list[GitLabDiscussionSchema]:
120
+ result = GitLabGetMRDiscussionsResponseSchema.model_validate_json(response.text)
121
+ return result.root
122
+
123
+ items = await paginate(
124
+ max_pages=settings.vcs.pagination.max_pages,
125
+ fetch_page=fetch_page,
126
+ extract_items=extract_items,
127
+ has_next_page=gitlab_has_next_page
128
+ )
129
+ return GitLabGetMRDiscussionsResponseSchema(root=items)
101
130
 
102
131
  async def create_note(
103
132
  self,
@@ -18,7 +18,8 @@ class GitLabDiscussionPositionSchema(BaseModel):
18
18
 
19
19
 
20
20
  class GitLabGetMRDiscussionsQuerySchema(BaseModel):
21
- per_page: int
21
+ page: int = 1
22
+ per_page: int = 100
22
23
 
23
24
 
24
25
  class GitLabGetMRDiscussionsResponseSchema(RootModel[list[GitLabDiscussionSchema]]):
@@ -7,7 +7,8 @@ class GitLabNoteSchema(BaseModel):
7
7
 
8
8
 
9
9
  class GitLabGetMRNotesQuerySchema(BaseModel):
10
- per_page: int
10
+ page: int = 1
11
+ per_page: int = 100
11
12
 
12
13
 
13
14
  class GitLabGetMRNotesResponseSchema(RootModel[list[GitLabNoteSchema]]):
@@ -0,0 +1,5 @@
1
+ from httpx import Response
2
+
3
+
4
+ def gitlab_has_next_page(response: Response) -> bool:
5
+ return bool(response.headers.get("X-Next-Page"))