xai-review 0.24.0__tar.gz → 0.26.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 (281) hide show
  1. {xai_review-0.24.0 → xai_review-0.26.0}/PKG-INFO +10 -6
  2. {xai_review-0.24.0 → xai_review-0.26.0}/README.md +9 -5
  3. xai_review-0.26.0/ai_review/clients/bitbucket/client.py +31 -0
  4. xai_review-0.26.0/ai_review/clients/bitbucket/pr/client.py +104 -0
  5. xai_review-0.26.0/ai_review/clients/bitbucket/pr/schema/comments.py +44 -0
  6. xai_review-0.26.0/ai_review/clients/bitbucket/pr/schema/files.py +25 -0
  7. xai_review-0.26.0/ai_review/clients/bitbucket/pr/schema/pull_request.py +38 -0
  8. xai_review-0.26.0/ai_review/clients/bitbucket/pr/types.py +44 -0
  9. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/claude/client.py +2 -1
  10. xai_review-0.26.0/ai_review/clients/claude/types.py +8 -0
  11. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/gemini/client.py +2 -1
  12. xai_review-0.26.0/ai_review/clients/gemini/types.py +8 -0
  13. xai_review-0.26.0/ai_review/clients/ollama/client.py +41 -0
  14. xai_review-0.26.0/ai_review/clients/ollama/schema.py +47 -0
  15. xai_review-0.26.0/ai_review/clients/ollama/types.py +8 -0
  16. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/openai/client.py +2 -1
  17. xai_review-0.26.0/ai_review/clients/openai/types.py +8 -0
  18. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/http.py +4 -1
  19. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/llm/base.py +8 -1
  20. xai_review-0.26.0/ai_review/libs/config/llm/claude.py +10 -0
  21. xai_review-0.26.0/ai_review/libs/config/llm/gemini.py +10 -0
  22. xai_review-0.26.0/ai_review/libs/config/llm/meta.py +7 -0
  23. xai_review-0.26.0/ai_review/libs/config/llm/ollama.py +14 -0
  24. xai_review-0.26.0/ai_review/libs/config/llm/openai.py +10 -0
  25. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/vcs/base.py +11 -1
  26. xai_review-0.26.0/ai_review/libs/config/vcs/bitbucket.py +13 -0
  27. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/vcs/github.py +2 -2
  28. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/vcs/gitlab.py +2 -2
  29. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/constants/llm_provider.py +1 -0
  30. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/constants/vcs_provider.py +1 -0
  31. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/llm/factory.py +3 -0
  32. xai_review-0.26.0/ai_review/services/llm/ollama/client.py +34 -0
  33. xai_review-0.26.0/ai_review/services/vcs/bitbucket/client.py +185 -0
  34. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/vcs/factory.py +3 -0
  35. xai_review-0.26.0/ai_review/tests/fixtures/clients/bitbucket.py +204 -0
  36. xai_review-0.26.0/ai_review/tests/fixtures/clients/claude.py +67 -0
  37. xai_review-0.26.0/ai_review/tests/fixtures/clients/gemini.py +73 -0
  38. xai_review-0.26.0/ai_review/tests/fixtures/clients/ollama.py +65 -0
  39. xai_review-0.26.0/ai_review/tests/fixtures/clients/openai.py +69 -0
  40. xai_review-0.26.0/ai_review/tests/suites/clients/ollama/test_client.py +12 -0
  41. xai_review-0.26.0/ai_review/tests/suites/clients/ollama/test_schema.py +65 -0
  42. xai_review-0.26.0/ai_review/tests/suites/services/llm/claude/test_client.py +22 -0
  43. xai_review-0.26.0/ai_review/tests/suites/services/llm/gemini/__init__.py +0 -0
  44. xai_review-0.26.0/ai_review/tests/suites/services/llm/gemini/test_client.py +22 -0
  45. xai_review-0.26.0/ai_review/tests/suites/services/llm/ollama/__init__.py +0 -0
  46. xai_review-0.26.0/ai_review/tests/suites/services/llm/ollama/test_client.py +22 -0
  47. xai_review-0.26.0/ai_review/tests/suites/services/llm/openai/__init__.py +0 -0
  48. xai_review-0.26.0/ai_review/tests/suites/services/llm/openai/test_client.py +22 -0
  49. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/llm/test_factory.py +8 -1
  50. xai_review-0.26.0/ai_review/tests/suites/services/prompt/__init__.py +0 -0
  51. xai_review-0.26.0/ai_review/tests/suites/services/review/__init__.py +0 -0
  52. xai_review-0.26.0/ai_review/tests/suites/services/review/inline/__init__.py +0 -0
  53. xai_review-0.26.0/ai_review/tests/suites/services/review/policy/__init__.py +0 -0
  54. xai_review-0.26.0/ai_review/tests/suites/services/review/summary/__init__.py +0 -0
  55. xai_review-0.26.0/ai_review/tests/suites/services/vcs/__init__.py +0 -0
  56. xai_review-0.26.0/ai_review/tests/suites/services/vcs/bitbucket/__init__.py +0 -0
  57. xai_review-0.26.0/ai_review/tests/suites/services/vcs/bitbucket/test_service.py +117 -0
  58. xai_review-0.26.0/ai_review/tests/suites/services/vcs/github/__init__.py +0 -0
  59. xai_review-0.26.0/ai_review/tests/suites/services/vcs/gitlab/__init__.py +0 -0
  60. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/vcs/test_factory.py +8 -1
  61. {xai_review-0.24.0 → xai_review-0.26.0}/pyproject.toml +1 -1
  62. {xai_review-0.24.0 → xai_review-0.26.0}/xai_review.egg-info/PKG-INFO +10 -6
  63. {xai_review-0.24.0 → xai_review-0.26.0}/xai_review.egg-info/SOURCES.txt +38 -0
  64. xai_review-0.24.0/ai_review/libs/config/llm/claude.py +0 -13
  65. xai_review-0.24.0/ai_review/libs/config/llm/gemini.py +0 -13
  66. xai_review-0.24.0/ai_review/libs/config/llm/openai.py +0 -13
  67. xai_review-0.24.0/ai_review/tests/fixtures/clients/claude.py +0 -22
  68. xai_review-0.24.0/ai_review/tests/fixtures/clients/gemini.py +0 -21
  69. xai_review-0.24.0/ai_review/tests/fixtures/clients/openai.py +0 -21
  70. {xai_review-0.24.0 → xai_review-0.26.0}/LICENSE +0 -0
  71. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/__init__.py +0 -0
  72. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/cli/__init__.py +0 -0
  73. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/cli/commands/__init__.py +0 -0
  74. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/cli/commands/run_context_review.py +0 -0
  75. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/cli/commands/run_inline_review.py +0 -0
  76. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/cli/commands/run_review.py +0 -0
  77. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/cli/commands/run_summary_review.py +0 -0
  78. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/cli/main.py +0 -0
  79. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/__init__.py +0 -0
  80. {xai_review-0.24.0/ai_review/clients/claude → xai_review-0.26.0/ai_review/clients/bitbucket}/__init__.py +0 -0
  81. {xai_review-0.24.0/ai_review/clients/gemini → xai_review-0.26.0/ai_review/clients/bitbucket/pr}/__init__.py +0 -0
  82. {xai_review-0.24.0/ai_review/clients/github → xai_review-0.26.0/ai_review/clients/bitbucket/pr/schema}/__init__.py +0 -0
  83. {xai_review-0.24.0/ai_review/clients/github/pr → xai_review-0.26.0/ai_review/clients/claude}/__init__.py +0 -0
  84. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/claude/schema.py +0 -0
  85. {xai_review-0.24.0/ai_review/clients/github/pr/schema → xai_review-0.26.0/ai_review/clients/gemini}/__init__.py +0 -0
  86. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/gemini/schema.py +0 -0
  87. {xai_review-0.24.0/ai_review/clients/gitlab → xai_review-0.26.0/ai_review/clients/github}/__init__.py +0 -0
  88. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/github/client.py +0 -0
  89. {xai_review-0.24.0/ai_review/clients/gitlab/mr → xai_review-0.26.0/ai_review/clients/github/pr}/__init__.py +0 -0
  90. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/github/pr/client.py +0 -0
  91. {xai_review-0.24.0/ai_review/clients/gitlab/mr → xai_review-0.26.0/ai_review/clients/github/pr}/schema/__init__.py +0 -0
  92. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/github/pr/schema/comments.py +0 -0
  93. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/github/pr/schema/files.py +0 -0
  94. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/github/pr/schema/pull_request.py +0 -0
  95. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/github/pr/schema/reviews.py +0 -0
  96. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/github/pr/types.py +0 -0
  97. {xai_review-0.24.0/ai_review/clients/openai → xai_review-0.26.0/ai_review/clients/gitlab}/__init__.py +0 -0
  98. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/gitlab/client.py +0 -0
  99. {xai_review-0.24.0/ai_review/libs → xai_review-0.26.0/ai_review/clients/gitlab/mr}/__init__.py +0 -0
  100. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/gitlab/mr/client.py +0 -0
  101. {xai_review-0.24.0/ai_review/libs/asynchronous → xai_review-0.26.0/ai_review/clients/gitlab/mr/schema}/__init__.py +0 -0
  102. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/gitlab/mr/schema/changes.py +0 -0
  103. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/gitlab/mr/schema/discussions.py +0 -0
  104. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/gitlab/mr/schema/notes.py +0 -0
  105. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/gitlab/mr/types.py +0 -0
  106. {xai_review-0.24.0/ai_review/libs/config → xai_review-0.26.0/ai_review/clients/ollama}/__init__.py +0 -0
  107. {xai_review-0.24.0/ai_review/libs/config/llm → xai_review-0.26.0/ai_review/clients/openai}/__init__.py +0 -0
  108. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/clients/openai/schema.py +0 -0
  109. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/config.py +0 -0
  110. {xai_review-0.24.0/ai_review/libs/config/vcs → xai_review-0.26.0/ai_review/libs}/__init__.py +0 -0
  111. {xai_review-0.24.0/ai_review/libs/constants → xai_review-0.26.0/ai_review/libs/asynchronous}/__init__.py +0 -0
  112. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/asynchronous/gather.py +0 -0
  113. {xai_review-0.24.0/ai_review/libs/diff → xai_review-0.26.0/ai_review/libs/config}/__init__.py +0 -0
  114. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/artifacts.py +0 -0
  115. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/base.py +0 -0
  116. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/core.py +0 -0
  117. {xai_review-0.24.0/ai_review/libs/http → xai_review-0.26.0/ai_review/libs/config/llm}/__init__.py +0 -0
  118. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/logger.py +0 -0
  119. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/prompt.py +0 -0
  120. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/config/review.py +0 -0
  121. {xai_review-0.24.0/ai_review/libs/http/event_hooks → xai_review-0.26.0/ai_review/libs/config/vcs}/__init__.py +0 -0
  122. {xai_review-0.24.0/ai_review/libs/http/transports → xai_review-0.26.0/ai_review/libs/constants}/__init__.py +0 -0
  123. {xai_review-0.24.0/ai_review/libs/template → xai_review-0.26.0/ai_review/libs/diff}/__init__.py +0 -0
  124. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/diff/models.py +0 -0
  125. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/diff/parser.py +0 -0
  126. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/diff/tools.py +0 -0
  127. {xai_review-0.24.0/ai_review/prompts → xai_review-0.26.0/ai_review/libs/http}/__init__.py +0 -0
  128. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/http/client.py +0 -0
  129. {xai_review-0.24.0/ai_review/resources → xai_review-0.26.0/ai_review/libs/http/event_hooks}/__init__.py +0 -0
  130. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/http/event_hooks/base.py +0 -0
  131. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/http/event_hooks/logger.py +0 -0
  132. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/http/handlers.py +0 -0
  133. {xai_review-0.24.0/ai_review/services → xai_review-0.26.0/ai_review/libs/http/transports}/__init__.py +0 -0
  134. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/http/transports/retry.py +0 -0
  135. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/json.py +0 -0
  136. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/logger.py +0 -0
  137. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/resources.py +0 -0
  138. {xai_review-0.24.0/ai_review/services/artifacts → xai_review-0.26.0/ai_review/libs/template}/__init__.py +0 -0
  139. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/libs/template/render.py +0 -0
  140. {xai_review-0.24.0/ai_review/services/cost → xai_review-0.26.0/ai_review/prompts}/__init__.py +0 -0
  141. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/prompts/default_context.md +0 -0
  142. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/prompts/default_inline.md +0 -0
  143. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/prompts/default_summary.md +0 -0
  144. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/prompts/default_system_context.md +0 -0
  145. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/prompts/default_system_inline.md +0 -0
  146. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/prompts/default_system_summary.md +0 -0
  147. {xai_review-0.24.0/ai_review/services/diff → xai_review-0.26.0/ai_review/resources}/__init__.py +0 -0
  148. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/resources/pricing.yaml +0 -0
  149. {xai_review-0.24.0/ai_review/services/git → xai_review-0.26.0/ai_review/services}/__init__.py +0 -0
  150. {xai_review-0.24.0/ai_review/services/llm → xai_review-0.26.0/ai_review/services/artifacts}/__init__.py +0 -0
  151. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/artifacts/schema.py +0 -0
  152. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/artifacts/service.py +0 -0
  153. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/artifacts/tools.py +0 -0
  154. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/artifacts/types.py +0 -0
  155. {xai_review-0.24.0/ai_review/services/llm/claude → xai_review-0.26.0/ai_review/services/cost}/__init__.py +0 -0
  156. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/cost/schema.py +0 -0
  157. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/cost/service.py +0 -0
  158. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/cost/types.py +0 -0
  159. {xai_review-0.24.0/ai_review/services/llm/gemini → xai_review-0.26.0/ai_review/services/diff}/__init__.py +0 -0
  160. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/diff/renderers.py +0 -0
  161. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/diff/schema.py +0 -0
  162. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/diff/service.py +0 -0
  163. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/diff/tools.py +0 -0
  164. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/diff/types.py +0 -0
  165. {xai_review-0.24.0/ai_review/services/llm/openai → xai_review-0.26.0/ai_review/services/git}/__init__.py +0 -0
  166. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/git/service.py +0 -0
  167. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/git/types.py +0 -0
  168. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/hook/__init__.py +0 -0
  169. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/hook/constants.py +0 -0
  170. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/hook/service.py +0 -0
  171. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/hook/types.py +0 -0
  172. {xai_review-0.24.0/ai_review/services/prompt → xai_review-0.26.0/ai_review/services/llm}/__init__.py +0 -0
  173. {xai_review-0.24.0/ai_review/services/review → xai_review-0.26.0/ai_review/services/llm/claude}/__init__.py +0 -0
  174. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/llm/claude/client.py +0 -0
  175. {xai_review-0.24.0/ai_review/services/review/gateway → xai_review-0.26.0/ai_review/services/llm/gemini}/__init__.py +0 -0
  176. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/llm/gemini/client.py +0 -0
  177. {xai_review-0.24.0/ai_review/services/review/inline → xai_review-0.26.0/ai_review/services/llm/ollama}/__init__.py +0 -0
  178. {xai_review-0.24.0/ai_review/services/review/policy → xai_review-0.26.0/ai_review/services/llm/openai}/__init__.py +0 -0
  179. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/llm/openai/client.py +0 -0
  180. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/llm/types.py +0 -0
  181. {xai_review-0.24.0/ai_review/services/review/summary → xai_review-0.26.0/ai_review/services/prompt}/__init__.py +0 -0
  182. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/prompt/adapter.py +0 -0
  183. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/prompt/schema.py +0 -0
  184. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/prompt/service.py +0 -0
  185. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/prompt/tools.py +0 -0
  186. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/prompt/types.py +0 -0
  187. {xai_review-0.24.0/ai_review/services/vcs → xai_review-0.26.0/ai_review/services/review}/__init__.py +0 -0
  188. {xai_review-0.24.0/ai_review/services/vcs/github → xai_review-0.26.0/ai_review/services/review/gateway}/__init__.py +0 -0
  189. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/gateway/comment.py +0 -0
  190. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/gateway/llm.py +0 -0
  191. {xai_review-0.24.0/ai_review/services/vcs/gitlab → xai_review-0.26.0/ai_review/services/review/inline}/__init__.py +0 -0
  192. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/inline/schema.py +0 -0
  193. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/inline/service.py +0 -0
  194. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/inline/types.py +0 -0
  195. {xai_review-0.24.0/ai_review/tests → xai_review-0.26.0/ai_review/services/review/policy}/__init__.py +0 -0
  196. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/policy/service.py +0 -0
  197. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/service.py +0 -0
  198. {xai_review-0.24.0/ai_review/tests/fixtures → xai_review-0.26.0/ai_review/services/review/summary}/__init__.py +0 -0
  199. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/summary/schema.py +0 -0
  200. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/summary/service.py +0 -0
  201. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/review/summary/types.py +0 -0
  202. {xai_review-0.24.0/ai_review/tests/fixtures/clients → xai_review-0.26.0/ai_review/services/vcs}/__init__.py +0 -0
  203. {xai_review-0.24.0/ai_review/tests/fixtures/services → xai_review-0.26.0/ai_review/services/vcs/bitbucket}/__init__.py +0 -0
  204. {xai_review-0.24.0/ai_review/tests/fixtures/services/review → xai_review-0.26.0/ai_review/services/vcs/github}/__init__.py +0 -0
  205. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/vcs/github/client.py +0 -0
  206. {xai_review-0.24.0/ai_review/tests/suites → xai_review-0.26.0/ai_review/services/vcs/gitlab}/__init__.py +0 -0
  207. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/vcs/gitlab/client.py +0 -0
  208. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/services/vcs/types.py +0 -0
  209. {xai_review-0.24.0/ai_review/tests/suites/clients → xai_review-0.26.0/ai_review/tests}/__init__.py +0 -0
  210. {xai_review-0.24.0/ai_review/tests/suites/clients/claude → xai_review-0.26.0/ai_review/tests/fixtures}/__init__.py +0 -0
  211. {xai_review-0.24.0/ai_review/tests/suites/clients/gemini → xai_review-0.26.0/ai_review/tests/fixtures/clients}/__init__.py +0 -0
  212. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/clients/github.py +0 -0
  213. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/clients/gitlab.py +0 -0
  214. {xai_review-0.24.0/ai_review/tests/suites/clients/github → xai_review-0.26.0/ai_review/tests/fixtures/services}/__init__.py +0 -0
  215. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/services/artifacts.py +0 -0
  216. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/services/cost.py +0 -0
  217. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/services/diff.py +0 -0
  218. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/services/git.py +0 -0
  219. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/services/llm.py +0 -0
  220. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/services/prompt.py +0 -0
  221. {xai_review-0.24.0/ai_review/tests/suites/clients/gitlab → xai_review-0.26.0/ai_review/tests/fixtures/services/review}/__init__.py +0 -0
  222. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/services/review/inline.py +0 -0
  223. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/services/review/summary.py +0 -0
  224. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/fixtures/services/vcs.py +0 -0
  225. {xai_review-0.24.0/ai_review/tests/suites/clients/openai → xai_review-0.26.0/ai_review/tests/suites}/__init__.py +0 -0
  226. {xai_review-0.24.0/ai_review/tests/suites/libs → xai_review-0.26.0/ai_review/tests/suites/clients}/__init__.py +0 -0
  227. {xai_review-0.24.0/ai_review/tests/suites/libs/asynchronous → xai_review-0.26.0/ai_review/tests/suites/clients/claude}/__init__.py +0 -0
  228. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/clients/claude/test_client.py +0 -0
  229. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/clients/claude/test_schema.py +0 -0
  230. {xai_review-0.24.0/ai_review/tests/suites/libs/config → xai_review-0.26.0/ai_review/tests/suites/clients/gemini}/__init__.py +0 -0
  231. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/clients/gemini/test_client.py +0 -0
  232. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/clients/gemini/test_schema.py +0 -0
  233. {xai_review-0.24.0/ai_review/tests/suites/libs/diff → xai_review-0.26.0/ai_review/tests/suites/clients/github}/__init__.py +0 -0
  234. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/clients/github/test_client.py +0 -0
  235. {xai_review-0.24.0/ai_review/tests/suites/libs/template → xai_review-0.26.0/ai_review/tests/suites/clients/gitlab}/__init__.py +0 -0
  236. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/clients/gitlab/test_client.py +0 -0
  237. {xai_review-0.24.0/ai_review/tests/suites/services → xai_review-0.26.0/ai_review/tests/suites/clients/ollama}/__init__.py +0 -0
  238. {xai_review-0.24.0/ai_review/tests/suites/services/cost → xai_review-0.26.0/ai_review/tests/suites/clients/openai}/__init__.py +0 -0
  239. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/clients/openai/test_client.py +0 -0
  240. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/clients/openai/test_schema.py +0 -0
  241. {xai_review-0.24.0/ai_review/tests/suites/services/diff → xai_review-0.26.0/ai_review/tests/suites/libs}/__init__.py +0 -0
  242. {xai_review-0.24.0/ai_review/tests/suites/services/hook → xai_review-0.26.0/ai_review/tests/suites/libs/asynchronous}/__init__.py +0 -0
  243. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/libs/asynchronous/test_gather.py +0 -0
  244. {xai_review-0.24.0/ai_review/tests/suites/services/llm → xai_review-0.26.0/ai_review/tests/suites/libs/config}/__init__.py +0 -0
  245. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/libs/config/test_prompt.py +0 -0
  246. {xai_review-0.24.0/ai_review/tests/suites/services/prompt → xai_review-0.26.0/ai_review/tests/suites/libs/diff}/__init__.py +0 -0
  247. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/libs/diff/test_models.py +0 -0
  248. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/libs/diff/test_parser.py +0 -0
  249. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/libs/diff/test_tools.py +0 -0
  250. {xai_review-0.24.0/ai_review/tests/suites/services/review → xai_review-0.26.0/ai_review/tests/suites/libs/template}/__init__.py +0 -0
  251. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/libs/template/test_render.py +0 -0
  252. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/libs/test_json.py +0 -0
  253. {xai_review-0.24.0/ai_review/tests/suites/services/review/inline → xai_review-0.26.0/ai_review/tests/suites/services}/__init__.py +0 -0
  254. {xai_review-0.24.0/ai_review/tests/suites/services/review/policy → xai_review-0.26.0/ai_review/tests/suites/services/cost}/__init__.py +0 -0
  255. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/cost/test_schema.py +0 -0
  256. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/cost/test_service.py +0 -0
  257. {xai_review-0.24.0/ai_review/tests/suites/services/review/summary → xai_review-0.26.0/ai_review/tests/suites/services/diff}/__init__.py +0 -0
  258. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/diff/test_renderers.py +0 -0
  259. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/diff/test_service.py +0 -0
  260. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/diff/test_tools.py +0 -0
  261. {xai_review-0.24.0/ai_review/tests/suites/services/vcs → xai_review-0.26.0/ai_review/tests/suites/services/hook}/__init__.py +0 -0
  262. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/hook/test_service.py +0 -0
  263. {xai_review-0.24.0/ai_review/tests/suites/services/vcs/github → xai_review-0.26.0/ai_review/tests/suites/services/llm}/__init__.py +0 -0
  264. {xai_review-0.24.0/ai_review/tests/suites/services/vcs/gitlab → xai_review-0.26.0/ai_review/tests/suites/services/llm/claude}/__init__.py +0 -0
  265. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/prompt/test_adapter.py +0 -0
  266. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/prompt/test_schema.py +0 -0
  267. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/prompt/test_service.py +0 -0
  268. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/prompt/test_tools.py +0 -0
  269. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/review/inline/test_schema.py +0 -0
  270. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/review/inline/test_service.py +0 -0
  271. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/review/policy/test_service.py +0 -0
  272. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/review/summary/test_schema.py +0 -0
  273. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/review/summary/test_service.py +0 -0
  274. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/review/test_service.py +0 -0
  275. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/vcs/github/test_service.py +0 -0
  276. {xai_review-0.24.0 → xai_review-0.26.0}/ai_review/tests/suites/services/vcs/gitlab/test_service.py +0 -0
  277. {xai_review-0.24.0 → xai_review-0.26.0}/setup.cfg +0 -0
  278. {xai_review-0.24.0 → xai_review-0.26.0}/xai_review.egg-info/dependency_links.txt +0 -0
  279. {xai_review-0.24.0 → xai_review-0.26.0}/xai_review.egg-info/entry_points.txt +0 -0
  280. {xai_review-0.24.0 → xai_review-0.26.0}/xai_review.egg-info/requires.txt +0 -0
  281. {xai_review-0.24.0 → xai_review-0.26.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.24.0
3
+ Version: 0.26.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>
@@ -66,8 +66,8 @@ improve code quality, enforce consistency, and speed up the review process.
66
66
 
67
67
  ✨ Key features:
68
68
 
69
- - **Multiple LLM providers** — choose between **OpenAI**, **Claude**, and **Gemini**, or switch anytime.
70
- - **VCS integration** — works out of the box with GitLab, GitHub (more providers coming).
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**, 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.
@@ -168,9 +168,9 @@ for complete, ready-to-use examples.
168
168
 
169
169
  Key things you can customize:
170
170
 
171
- - **LLM provider** — OpenAI, Gemini, or Claude
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.24.0
212
+ - uses: Nikita-Filonov/ai-review@v0.26.0
213
213
  with:
214
214
  review-command: ${{ inputs.review-command }}
215
215
  env:
@@ -288,6 +288,10 @@ provider** explicitly configured in your `.ai-review.yaml`.
288
288
  All data is sent **directly** from your CI/CD environment to the selected LLM API endpoint (e.g. OpenAI, Gemini,
289
289
  Claude). No intermediary servers or storage layers are involved.
290
290
 
291
+ If you use **Ollama**, requests are sent to your **local or self-hosted Ollama runtime**
292
+ (by default `http://localhost:11434`). This allows you to run reviews completely **offline**, keeping all data strictly
293
+ inside your infrastructure.
294
+
291
295
  > ⚠️ Please ensure you use proper API tokens and avoid exposing corporate or personal secrets.
292
296
  > If you accidentally leak private code or credentials due to incorrect configuration (e.g., using a personal key
293
297
  > instead of an enterprise one), it is **your responsibility** — the tool does not retain or share any data by itself.
@@ -32,8 +32,8 @@ improve code quality, enforce consistency, and speed up the review process.
32
32
 
33
33
  ✨ Key features:
34
34
 
35
- - **Multiple LLM providers** — choose between **OpenAI**, **Claude**, and **Gemini**, or switch anytime.
36
- - **VCS integration** — works out of the box with GitLab, GitHub (more providers coming).
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**, 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.
@@ -134,9 +134,9 @@ for complete, ready-to-use examples.
134
134
 
135
135
  Key things you can customize:
136
136
 
137
- - **LLM provider** — OpenAI, Gemini, or Claude
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.24.0
178
+ - uses: Nikita-Filonov/ai-review@v0.26.0
179
179
  with:
180
180
  review-command: ${{ inputs.review-command }}
181
181
  env:
@@ -254,6 +254,10 @@ provider** explicitly configured in your `.ai-review.yaml`.
254
254
  All data is sent **directly** from your CI/CD environment to the selected LLM API endpoint (e.g. OpenAI, Gemini,
255
255
  Claude). No intermediary servers or storage layers are involved.
256
256
 
257
+ If you use **Ollama**, requests are sent to your **local or self-hosted Ollama runtime**
258
+ (by default `http://localhost:11434`). This allows you to run reviews completely **offline**, keeping all data strictly
259
+ inside your infrastructure.
260
+
257
261
  > ⚠️ Please ensure you use proper API tokens and avoid exposing corporate or personal secrets.
258
262
  > If you accidentally leak private code or credentials due to incorrect configuration (e.g., using a personal key
259
263
  > instead of an enterprise one), it is **your responsibility** — the tool does not retain or share any data by itself.
@@ -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,104 @@
1
+ from httpx import Response, QueryParams
2
+
3
+ from ai_review.clients.bitbucket.pr.schema.comments import (
4
+ BitbucketGetPRCommentsQuerySchema,
5
+ BitbucketGetPRCommentsResponseSchema,
6
+ BitbucketCreatePRCommentRequestSchema,
7
+ BitbucketCreatePRCommentResponseSchema,
8
+ )
9
+ from ai_review.clients.bitbucket.pr.schema.files import (
10
+ BitbucketGetPRFilesQuerySchema,
11
+ BitbucketGetPRFilesResponseSchema,
12
+ )
13
+ from ai_review.clients.bitbucket.pr.schema.pull_request import BitbucketGetPRResponseSchema
14
+ from ai_review.clients.bitbucket.pr.types import BitbucketPullRequestsHTTPClientProtocol
15
+ from ai_review.libs.http.client import HTTPClient
16
+ from ai_review.libs.http.handlers import handle_http_error, HTTPClientError
17
+
18
+
19
+ class BitbucketPullRequestsHTTPClientError(HTTPClientError):
20
+ pass
21
+
22
+
23
+ class BitbucketPullRequestsHTTPClient(HTTPClient, BitbucketPullRequestsHTTPClientProtocol):
24
+ @handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
25
+ async def get_pull_request_api(self, workspace: str, repo_slug: str, pull_request_id: str) -> Response:
26
+ return await self.get(f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}")
27
+
28
+ @handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
29
+ async def get_diffstat_api(
30
+ self,
31
+ workspace: str,
32
+ repo_slug: str,
33
+ pull_request_id: str,
34
+ query: BitbucketGetPRFilesQuerySchema,
35
+ ) -> Response:
36
+ return await self.get(
37
+ f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/diffstat",
38
+ query=QueryParams(**query.model_dump()),
39
+ )
40
+
41
+ @handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
42
+ async def get_comments_api(
43
+ self,
44
+ workspace: str,
45
+ repo_slug: str,
46
+ pull_request_id: str,
47
+ query: BitbucketGetPRCommentsQuerySchema,
48
+ ) -> Response:
49
+ return await self.get(
50
+ f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/comments",
51
+ query=QueryParams(**query.model_dump()),
52
+ )
53
+
54
+ @handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
55
+ async def create_comment_api(
56
+ self,
57
+ workspace: str,
58
+ repo_slug: str,
59
+ pull_request_id: str,
60
+ request: BitbucketCreatePRCommentRequestSchema,
61
+ ) -> Response:
62
+ return await self.post(
63
+ f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/comments",
64
+ json=request.model_dump(by_alias=True),
65
+ )
66
+
67
+ async def get_pull_request(
68
+ self,
69
+ workspace: str,
70
+ repo_slug: str,
71
+ pull_request_id: str
72
+ ) -> BitbucketGetPRResponseSchema:
73
+ resp = await self.get_pull_request_api(workspace, repo_slug, pull_request_id)
74
+ return BitbucketGetPRResponseSchema.model_validate_json(resp.text)
75
+
76
+ async def get_files(
77
+ self,
78
+ workspace: str,
79
+ repo_slug: str,
80
+ pull_request_id: str
81
+ ) -> BitbucketGetPRFilesResponseSchema:
82
+ query = BitbucketGetPRFilesQuerySchema(pagelen=100)
83
+ resp = await self.get_diffstat_api(workspace, repo_slug, pull_request_id, query)
84
+ return BitbucketGetPRFilesResponseSchema.model_validate_json(resp.text)
85
+
86
+ async def get_comments(
87
+ self,
88
+ workspace: str,
89
+ repo_slug: str,
90
+ pull_request_id: str
91
+ ) -> BitbucketGetPRCommentsResponseSchema:
92
+ query = BitbucketGetPRCommentsQuerySchema(pagelen=100)
93
+ response = await self.get_comments_api(workspace, repo_slug, pull_request_id, query)
94
+ return BitbucketGetPRCommentsResponseSchema.model_validate_json(response.text)
95
+
96
+ async def create_comment(
97
+ self,
98
+ workspace: str,
99
+ repo_slug: str,
100
+ pull_request_id: str,
101
+ request: BitbucketCreatePRCommentRequestSchema
102
+ ) -> BitbucketCreatePRCommentResponseSchema:
103
+ response = await self.create_comment_api(workspace, repo_slug, pull_request_id, request)
104
+ return BitbucketCreatePRCommentResponseSchema.model_validate_json(response.text)
@@ -0,0 +1,44 @@
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
+ pagelen: int = 100
26
+
27
+
28
+ class BitbucketGetPRCommentsResponseSchema(BaseModel):
29
+ size: int
30
+ page: int | None = None
31
+ next: str | None = None
32
+ values: list[BitbucketPRCommentSchema]
33
+ pagelen: int
34
+
35
+
36
+ class BitbucketCreatePRCommentRequestSchema(BaseModel):
37
+ inline: BitbucketCommentInlineSchema | None = None
38
+ content: BitbucketCommentContentSchema
39
+
40
+
41
+ class BitbucketCreatePRCommentResponseSchema(BaseModel):
42
+ id: int
43
+ inline: BitbucketCommentInlineSchema | None = None
44
+ content: BitbucketCommentContentSchema
@@ -0,0 +1,25 @@
1
+ from pydantic import BaseModel
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
+ pagelen: int = 100
18
+
19
+
20
+ class BitbucketGetPRFilesResponseSchema(BaseModel):
21
+ size: int
22
+ page: int | None = None
23
+ next: str | None = None
24
+ values: list[BitbucketPRFileSchema]
25
+ pagelen: int
@@ -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
+ ...
@@ -1,6 +1,7 @@
1
1
  from httpx import AsyncClient, Response, AsyncHTTPTransport
2
2
 
3
3
  from ai_review.clients.claude.schema import ClaudeChatRequestSchema, ClaudeChatResponseSchema
4
+ from ai_review.clients.claude.types import ClaudeHTTPClientProtocol
4
5
  from ai_review.config import settings
5
6
  from ai_review.libs.http.client import HTTPClient
6
7
  from ai_review.libs.http.event_hooks.logger import LoggerEventHook
@@ -13,7 +14,7 @@ class ClaudeHTTPClientError(HTTPClientError):
13
14
  pass
14
15
 
15
16
 
16
- class ClaudeHTTPClient(HTTPClient):
17
+ class ClaudeHTTPClient(HTTPClient, ClaudeHTTPClientProtocol):
17
18
  @handle_http_error(client="ClaudeHTTPClient", exception=ClaudeHTTPClientError)
18
19
  async def chat_api(self, request: ClaudeChatRequestSchema) -> Response:
19
20
  return await self.post("/v1/messages", json=request.model_dump())
@@ -0,0 +1,8 @@
1
+ from typing import Protocol
2
+
3
+ from ai_review.clients.claude.schema import ClaudeChatRequestSchema, ClaudeChatResponseSchema
4
+
5
+
6
+ class ClaudeHTTPClientProtocol(Protocol):
7
+ async def chat(self, request: ClaudeChatRequestSchema) -> ClaudeChatResponseSchema:
8
+ ...
@@ -1,6 +1,7 @@
1
1
  from httpx import Response, AsyncHTTPTransport, AsyncClient
2
2
 
3
3
  from ai_review.clients.gemini.schema import GeminiChatRequestSchema, GeminiChatResponseSchema
4
+ from ai_review.clients.gemini.types import GeminiHTTPClientProtocol
4
5
  from ai_review.config import settings
5
6
  from ai_review.libs.http.client import HTTPClient
6
7
  from ai_review.libs.http.event_hooks.logger import LoggerEventHook
@@ -13,7 +14,7 @@ class GeminiHTTPClientError(HTTPClientError):
13
14
  pass
14
15
 
15
16
 
16
- class GeminiHTTPClient(HTTPClient):
17
+ class GeminiHTTPClient(HTTPClient, GeminiHTTPClientProtocol):
17
18
  @handle_http_error(client="GeminiHTTPClient", exception=GeminiHTTPClientError)
18
19
  async def chat_api(self, request: GeminiChatRequestSchema) -> Response:
19
20
  meta = settings.llm.meta
@@ -0,0 +1,8 @@
1
+ from typing import Protocol
2
+
3
+ from ai_review.clients.gemini.schema import GeminiChatRequestSchema, GeminiChatResponseSchema
4
+
5
+
6
+ class GeminiHTTPClientProtocol(Protocol):
7
+ async def chat(self, request: GeminiChatRequestSchema) -> GeminiChatResponseSchema:
8
+ ...
@@ -0,0 +1,41 @@
1
+ from httpx import AsyncClient, Response, AsyncHTTPTransport
2
+
3
+ from ai_review.clients.ollama.schema import OllamaChatRequestSchema, OllamaChatResponseSchema
4
+ from ai_review.clients.ollama.types import OllamaHTTPClientProtocol
5
+ from ai_review.config import settings
6
+ from ai_review.libs.http.client import HTTPClient
7
+ from ai_review.libs.http.event_hooks.logger import LoggerEventHook
8
+ from ai_review.libs.http.handlers import HTTPClientError, handle_http_error
9
+ from ai_review.libs.http.transports.retry import RetryTransport
10
+ from ai_review.libs.logger import get_logger
11
+
12
+
13
+ class OllamaHTTPClientError(HTTPClientError):
14
+ pass
15
+
16
+
17
+ class OllamaHTTPClient(HTTPClient, OllamaHTTPClientProtocol):
18
+ @handle_http_error(client="OllamaHTTPClient", exception=OllamaHTTPClientError)
19
+ async def chat_api(self, request: OllamaChatRequestSchema) -> Response:
20
+ return await self.post("/api/chat", json=request.model_dump())
21
+
22
+ async def chat(self, request: OllamaChatRequestSchema) -> OllamaChatResponseSchema:
23
+ response = await self.chat_api(request)
24
+ return OllamaChatResponseSchema.model_validate_json(response.text)
25
+
26
+
27
+ def get_ollama_http_client() -> OllamaHTTPClient:
28
+ logger = get_logger("OLLAMA_HTTP_CLIENT")
29
+ logger_event_hook = LoggerEventHook(logger=logger)
30
+ retry_transport = RetryTransport(logger=logger, transport=AsyncHTTPTransport())
31
+
32
+ client = AsyncClient(
33
+ timeout=settings.llm.http_client.timeout,
34
+ base_url=settings.llm.http_client.api_url_value,
35
+ transport=retry_transport,
36
+ event_hooks={
37
+ "request": [logger_event_hook.request],
38
+ "response": [logger_event_hook.response],
39
+ },
40
+ )
41
+ return OllamaHTTPClient(client=client)
@@ -0,0 +1,47 @@
1
+ from typing import Literal
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class OllamaMessageSchema(BaseModel):
7
+ role: Literal["system", "user", "assistant"]
8
+ content: str
9
+
10
+
11
+ class OllamaOptionsSchema(BaseModel):
12
+ stop: list[str] | None = None
13
+ seed: int | None = None
14
+ top_p: float | None = Field(default=None, ge=0.0, le=1.0)
15
+ temperature: float | None = Field(default=None, ge=0.0, le=2.0)
16
+ num_predict: int | None = Field(default=None, ge=1)
17
+ repeat_penalty: float | None = Field(default=None, ge=0.0)
18
+
19
+
20
+ class OllamaChatRequestSchema(BaseModel):
21
+ model: str
22
+ stream: bool = False
23
+ options: OllamaOptionsSchema | None = None
24
+ messages: list[OllamaMessageSchema]
25
+
26
+
27
+ class OllamaUsageSchema(BaseModel):
28
+ prompt_tokens: int | None = None
29
+ completion_tokens: int | None = None
30
+
31
+ @property
32
+ def total_tokens(self) -> int | None:
33
+ if (self.prompt_tokens is not None) and (self.completion_tokens is not None):
34
+ return self.prompt_tokens + self.completion_tokens
35
+
36
+ return None
37
+
38
+
39
+ class OllamaChatResponseSchema(BaseModel):
40
+ done: bool = Field(default=True)
41
+ usage: OllamaUsageSchema | None = None
42
+ model: str
43
+ message: OllamaMessageSchema
44
+
45
+ @property
46
+ def first_text(self) -> str:
47
+ return (self.message.content or "").strip()
@@ -0,0 +1,8 @@
1
+ from typing import Protocol
2
+
3
+ from ai_review.clients.ollama.schema import OllamaChatRequestSchema, OllamaChatResponseSchema
4
+
5
+
6
+ class OllamaHTTPClientProtocol(Protocol):
7
+ async def chat(self, request: OllamaChatRequestSchema) -> OllamaChatResponseSchema:
8
+ ...
@@ -1,6 +1,7 @@
1
1
  from httpx import Response, AsyncHTTPTransport, AsyncClient
2
2
 
3
3
  from ai_review.clients.openai.schema import OpenAIChatRequestSchema, OpenAIChatResponseSchema
4
+ from ai_review.clients.openai.types import OpenAIHTTPClientProtocol
4
5
  from ai_review.config import settings
5
6
  from ai_review.libs.http.client import HTTPClient
6
7
  from ai_review.libs.http.event_hooks.logger import LoggerEventHook
@@ -13,7 +14,7 @@ class OpenAIHTTPClientError(HTTPClientError):
13
14
  pass
14
15
 
15
16
 
16
- class OpenAIHTTPClient(HTTPClient):
17
+ class OpenAIHTTPClient(HTTPClient, OpenAIHTTPClientProtocol):
17
18
  @handle_http_error(client='OpenAIHTTPClient', exception=OpenAIHTTPClientError)
18
19
  async def chat_api(self, request: OpenAIChatRequestSchema) -> Response:
19
20
  return await self.post("/chat/completions", json=request.model_dump())
@@ -0,0 +1,8 @@
1
+ from typing import Protocol
2
+
3
+ from ai_review.clients.openai.schema import OpenAIChatRequestSchema, OpenAIChatResponseSchema
4
+
5
+
6
+ class OpenAIHTTPClientProtocol(Protocol):
7
+ async def chat(self, request: OpenAIChatRequestSchema) -> OpenAIChatResponseSchema:
8
+ ...
@@ -4,12 +4,15 @@ from pydantic import BaseModel, HttpUrl, SecretStr
4
4
  class HTTPClientConfig(BaseModel):
5
5
  timeout: float = 120
6
6
  api_url: HttpUrl
7
- api_token: SecretStr
8
7
 
9
8
  @property
10
9
  def api_url_value(self) -> str:
11
10
  return str(self.api_url)
12
11
 
12
+
13
+ class HTTPClientWithTokenConfig(HTTPClientConfig):
14
+ api_token: SecretStr
15
+
13
16
  @property
14
17
  def api_token_value(self) -> str:
15
18
  return self.api_token.get_secret_value()
@@ -6,6 +6,7 @@ from pydantic import BaseModel, Field, FilePath
6
6
 
7
7
  from ai_review.libs.config.llm.claude import ClaudeHTTPClientConfig, ClaudeMetaConfig
8
8
  from ai_review.libs.config.llm.gemini import GeminiHTTPClientConfig, GeminiMetaConfig
9
+ from ai_review.libs.config.llm.ollama import OllamaHTTPClientConfig, OllamaMetaConfig
9
10
  from ai_review.libs.config.llm.openai import OpenAIHTTPClientConfig, OpenAIMetaConfig
10
11
  from ai_review.libs.constants.llm_provider import LLMProvider
11
12
  from ai_review.libs.resources import load_resource
@@ -55,7 +56,13 @@ class ClaudeLLMConfig(LLMConfigBase):
55
56
  http_client: ClaudeHTTPClientConfig
56
57
 
57
58
 
59
+ class OllamaLLMConfig(LLMConfigBase):
60
+ meta: OllamaMetaConfig
61
+ provider: Literal[LLMProvider.OLLAMA]
62
+ http_client: OllamaHTTPClientConfig
63
+
64
+
58
65
  LLMConfig = Annotated[
59
- OpenAILLMConfig | GeminiLLMConfig | ClaudeLLMConfig,
66
+ OpenAILLMConfig | GeminiLLMConfig | ClaudeLLMConfig | OllamaLLMConfig,
60
67
  Field(discriminator="provider")
61
68
  ]
@@ -0,0 +1,10 @@
1
+ from ai_review.libs.config.http import HTTPClientWithTokenConfig
2
+ from ai_review.libs.config.llm.meta import LLMMetaConfig
3
+
4
+
5
+ class ClaudeMetaConfig(LLMMetaConfig):
6
+ model: str = "claude-3-sonnet"
7
+
8
+
9
+ class ClaudeHTTPClientConfig(HTTPClientWithTokenConfig):
10
+ api_version: str = "2023-06-01"
@@ -0,0 +1,10 @@
1
+ from ai_review.libs.config.http import HTTPClientWithTokenConfig
2
+ from ai_review.libs.config.llm.meta import LLMMetaConfig
3
+
4
+
5
+ class GeminiMetaConfig(LLMMetaConfig):
6
+ model: str = "gemini-2.0-pro"
7
+
8
+
9
+ class GeminiHTTPClientConfig(HTTPClientWithTokenConfig):
10
+ pass
@@ -0,0 +1,7 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ class LLMMetaConfig(BaseModel):
5
+ model: str
6
+ max_tokens: int = Field(default=5000, ge=1)
7
+ temperature: float = Field(default=0.3, ge=0.0, le=2.0)
@@ -0,0 +1,14 @@
1
+ from ai_review.libs.config.http import HTTPClientConfig
2
+ from ai_review.libs.config.llm.meta import LLMMetaConfig
3
+
4
+
5
+ class OllamaMetaConfig(LLMMetaConfig):
6
+ stop: list[str] | None = None
7
+ seed: int | None = None
8
+ model: str = "llama2"
9
+ top_p: float | None = None
10
+ repeat_penalty: float | None = None
11
+
12
+
13
+ class OllamaHTTPClientConfig(HTTPClientConfig):
14
+ pass
@@ -0,0 +1,10 @@
1
+ from ai_review.libs.config.http import HTTPClientWithTokenConfig
2
+ from ai_review.libs.config.llm.meta import LLMMetaConfig
3
+
4
+
5
+ class OpenAIMetaConfig(LLMMetaConfig):
6
+ model: str = "gpt-4o-mini"
7
+
8
+
9
+ class OpenAIHTTPClientConfig(HTTPClientWithTokenConfig):
10
+ pass