xai-review 0.18.0__py3-none-any.whl → 0.19.0__py3-none-any.whl

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 (27) hide show
  1. ai_review/clients/github/pr/client.py +13 -7
  2. ai_review/clients/github/pr/schema/pull_request.py +6 -6
  3. ai_review/clients/gitlab/mr/client.py +29 -20
  4. ai_review/clients/gitlab/mr/schema/changes.py +5 -5
  5. ai_review/clients/gitlab/mr/schema/discussions.py +1 -4
  6. ai_review/clients/gitlab/mr/schema/notes.py +19 -0
  7. ai_review/services/llm/factory.py +1 -1
  8. ai_review/services/prompt/adapter.py +15 -15
  9. ai_review/services/prompt/schema.py +18 -18
  10. ai_review/services/review/service.py +45 -42
  11. ai_review/services/vcs/factory.py +1 -1
  12. ai_review/services/vcs/github/client.py +52 -34
  13. ai_review/services/vcs/gitlab/client.py +62 -44
  14. ai_review/services/vcs/types.py +38 -29
  15. ai_review/tests/suites/services/cost/__init__.py +0 -0
  16. ai_review/tests/suites/services/cost/test_schema.py +124 -0
  17. ai_review/tests/suites/services/cost/test_service.py +99 -0
  18. ai_review/tests/suites/services/prompt/test_adapter.py +59 -0
  19. ai_review/tests/suites/services/prompt/test_schema.py +18 -18
  20. ai_review/tests/suites/services/prompt/test_service.py +13 -11
  21. {xai_review-0.18.0.dist-info → xai_review-0.19.0.dist-info}/METADATA +21 -6
  22. {xai_review-0.18.0.dist-info → xai_review-0.19.0.dist-info}/RECORD +26 -22
  23. ai_review/clients/gitlab/mr/schema/comments.py +0 -19
  24. {xai_review-0.18.0.dist-info → xai_review-0.19.0.dist-info}/WHEEL +0 -0
  25. {xai_review-0.18.0.dist-info → xai_review-0.19.0.dist-info}/entry_points.txt +0 -0
  26. {xai_review-0.18.0.dist-info → xai_review-0.19.0.dist-info}/licenses/LICENSE +0 -0
  27. {xai_review-0.18.0.dist-info → xai_review-0.19.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,99 @@
1
+ import pytest
2
+
3
+ from ai_review.libs.config.llm import LLMPricingConfig, LLMConfigBase
4
+ from ai_review.services.cost.schema import CostReportSchema
5
+ from ai_review.services.cost.service import CostService
6
+ from ai_review.services.llm.types import ChatResultSchema
7
+
8
+
9
+ # ---------- fixtures ----------
10
+
11
+ @pytest.fixture(autouse=True)
12
+ def fake_pricing(monkeypatch: pytest.MonkeyPatch):
13
+ monkeypatch.setattr(
14
+ LLMConfigBase,
15
+ "load_pricing",
16
+ lambda self: {"gpt-4-test": LLMPricingConfig(input=0.000001, output=0.000002)}
17
+ )
18
+ monkeypatch.setattr("ai_review.config.settings.llm.meta.model", "gpt-4-test")
19
+
20
+
21
+ @pytest.fixture
22
+ def cost_service() -> CostService:
23
+ return CostService()
24
+
25
+
26
+ @pytest.fixture
27
+ def sample_result() -> ChatResultSchema:
28
+ return ChatResultSchema(text="result", prompt_tokens=1000, completion_tokens=500)
29
+
30
+
31
+ # ---------- tests: CALCULATE ----------
32
+
33
+ def test_calculate_basic(cost_service: CostService, sample_result: ChatResultSchema) -> None:
34
+ """
35
+ Should correctly calculate input/output/total cost and return report.
36
+ """
37
+ report = cost_service.calculate(sample_result)
38
+
39
+ assert isinstance(report, CostReportSchema)
40
+ assert report.model == "gpt-4-test"
41
+ assert pytest.approx(report.input_cost, 1e-8) == 0.001
42
+ assert pytest.approx(report.output_cost, 1e-8) == 0.001
43
+ assert pytest.approx(report.total_cost, 1e-8) == 0.002
44
+
45
+ assert len(cost_service.reports) == 1
46
+
47
+
48
+ def test_calculate_missing_tokens(cost_service: CostService) -> None:
49
+ """
50
+ Should return None if prompt_tokens or completion_tokens is None.
51
+ """
52
+ result = ChatResultSchema(text="result", prompt_tokens=None, completion_tokens=123)
53
+ assert cost_service.calculate(result) is None
54
+
55
+ result = ChatResultSchema(text="result", prompt_tokens=100, completion_tokens=None)
56
+ assert cost_service.calculate(result) is None
57
+
58
+
59
+ def test_calculate_no_pricing(monkeypatch: pytest.MonkeyPatch) -> None:
60
+ """
61
+ Should return None and log warning if pricing not found.
62
+ """
63
+ monkeypatch.setattr("ai_review.config.settings.llm.meta.model", "unknown-model")
64
+ monkeypatch.setattr(LLMConfigBase, "load_pricing", lambda self: {})
65
+
66
+ service = CostService()
67
+ result = ChatResultSchema(text="result", prompt_tokens=10, completion_tokens=5)
68
+ out = service.calculate(result)
69
+
70
+ assert out is None
71
+ assert service.reports == []
72
+
73
+
74
+ # ---------- tests: AGGREGATE ----------
75
+
76
+ def test_aggregate_empty(cost_service: CostService) -> None:
77
+ """
78
+ Should return None when no reports exist.
79
+ """
80
+ assert cost_service.aggregate() is None
81
+
82
+
83
+ def test_aggregate_combines_multiple_reports(cost_service: CostService, sample_result: ChatResultSchema) -> None:
84
+ """
85
+ Should combine multiple cost reports into a single aggregated summary.
86
+ """
87
+ cost_service.calculate(sample_result)
88
+ cost_service.calculate(sample_result)
89
+
90
+ agg = cost_service.aggregate()
91
+ assert isinstance(agg, CostReportSchema)
92
+
93
+ assert agg.prompt_tokens == 2000
94
+ assert agg.completion_tokens == 1000
95
+ assert pytest.approx(agg.input_cost, 1e-8) == 0.002
96
+ assert pytest.approx(agg.output_cost, 1e-8) == 0.002
97
+ assert pytest.approx(agg.total_cost, 1e-8) == 0.004
98
+
99
+ assert agg.model == "gpt-4-test"
@@ -0,0 +1,59 @@
1
+ from ai_review.services.prompt.adapter import build_prompt_context_from_mr_info
2
+ from ai_review.services.vcs.types import (
3
+ ReviewInfoSchema,
4
+ UserSchema,
5
+ BranchRefSchema,
6
+ )
7
+
8
+
9
+ def test_build_prompt_context_from_full_review_info() -> None:
10
+ review_info = ReviewInfoSchema(
11
+ id=42,
12
+ title="Fix API bug",
13
+ description="Refactored endpoint",
14
+ author=UserSchema(id=1, name="Alice", username="alice"),
15
+ reviewers=[
16
+ UserSchema(id=2, name="Bob", username="bob"),
17
+ UserSchema(id=3, name="Charlie", username="charlie"),
18
+ ],
19
+ assignees=[UserSchema(id=4, name="Dave", username="dave")],
20
+ source_branch=BranchRefSchema(ref="feature/fix-api", sha="123abc"),
21
+ target_branch=BranchRefSchema(ref="main", sha="456def"),
22
+ labels=["bug", "backend"],
23
+ changed_files=["api/views.py", "api/tests.py"],
24
+ )
25
+
26
+ context = build_prompt_context_from_mr_info(review_info)
27
+
28
+ assert context.review_title == "Fix API bug"
29
+ assert context.review_description == "Refactored endpoint"
30
+
31
+ assert context.review_author_name == "Alice"
32
+ assert context.review_author_username == "alice"
33
+
34
+ assert context.review_reviewers == ["Bob", "Charlie"]
35
+ assert context.review_reviewers_usernames == ["bob", "charlie"]
36
+ assert context.review_reviewer == "Bob" # первый ревьюер выбран корректно
37
+
38
+ assert context.review_assignees == ["Dave"]
39
+ assert context.review_assignees_usernames == ["dave"]
40
+
41
+ assert context.source_branch == "feature/fix-api"
42
+ assert context.target_branch == "main"
43
+
44
+ assert context.labels == ["bug", "backend"]
45
+ assert context.changed_files == ["api/views.py", "api/tests.py"]
46
+
47
+
48
+ def test_build_prompt_context_handles_no_reviewers() -> None:
49
+ review_info = ReviewInfoSchema(
50
+ title="Empty reviewers test",
51
+ author=UserSchema(name="Alice", username="alice"),
52
+ reviewers=[],
53
+ )
54
+
55
+ context = build_prompt_context_from_mr_info(review_info)
56
+
57
+ assert context.review_reviewer == ""
58
+ assert context.review_reviewers == []
59
+ assert context.review_reviewers_usernames == []
@@ -6,24 +6,24 @@ from ai_review.services.prompt.schema import PromptContextSchema
6
6
 
7
7
  def test_apply_format_inserts_variables() -> None:
8
8
  context = PromptContextSchema(
9
- merge_request_title="My MR",
10
- merge_request_author_username="nikita"
9
+ review_title="My Review",
10
+ review_author_username="nikita"
11
11
  )
12
- template = "Title: <<merge_request_title>>, Author: @<<merge_request_author_username>>"
12
+ template = "Title: <<review_title>>, Author: @<<review_author_username>>"
13
13
  result = context.apply_format(template)
14
- assert result == "Title: My MR, Author: @nikita"
14
+ assert result == "Title: My Review, Author: @nikita"
15
15
 
16
16
 
17
17
  def test_apply_format_with_lists() -> None:
18
18
  context = PromptContextSchema(
19
- merge_request_reviewers=["Alice", "Bob"],
20
- merge_request_reviewers_usernames=["alice", "bob"],
19
+ review_reviewers=["Alice", "Bob"],
20
+ review_reviewers_usernames=["alice", "bob"],
21
21
  labels=["bug", "feature"],
22
22
  changed_files=["a.py", "b.py"],
23
23
  )
24
24
  template = (
25
- "Reviewers: <<merge_request_reviewers>>\n"
26
- "Usernames: <<merge_request_reviewers_usernames>>\n"
25
+ "Reviewers: <<review_reviewers>>\n"
26
+ "Usernames: <<review_reviewers_usernames>>\n"
27
27
  "Labels: <<labels>>\n"
28
28
  "Files: <<changed_files>>"
29
29
  )
@@ -36,7 +36,7 @@ def test_apply_format_with_lists() -> None:
36
36
 
37
37
  def test_apply_format_handles_missing_fields() -> None:
38
38
  context = PromptContextSchema()
39
- template = "Title: <<merge_request_title>>, Reviewer: <<merge_request_reviewer>>"
39
+ template = "Title: <<review_title>>, Reviewer: <<review_reviewer>>"
40
40
  result = context.apply_format(template)
41
41
  assert result == "Title: , Reviewer: "
42
42
 
@@ -49,23 +49,23 @@ def test_apply_format_unknown_placeholder_kept() -> None:
49
49
 
50
50
 
51
51
  def test_apply_format_multiple_occurrences() -> None:
52
- context = PromptContextSchema(merge_request_title="My MR")
53
- template = "<<merge_request_title>> again <<merge_request_title>>"
52
+ context = PromptContextSchema(review_title="My Review")
53
+ template = "<<review_title>> again <<review_title>>"
54
54
  result = context.apply_format(template)
55
- assert result == "My MR again My MR"
55
+ assert result == "My Review again My Review"
56
56
 
57
57
 
58
58
  def test_apply_format_override_from_settings(monkeypatch: pytest.MonkeyPatch) -> None:
59
- monkeypatch.setitem(settings.prompt.context, "merge_request_title", "Overridden")
60
- context = PromptContextSchema(merge_request_title="Local Value")
61
- template = "Title: <<merge_request_title>>"
59
+ monkeypatch.setitem(settings.prompt.context, "review_title", "Overridden")
60
+ context = PromptContextSchema(review_title="Local Value")
61
+ template = "Title: <<review_title>>"
62
62
  result = context.apply_format(template)
63
63
  assert result == "Title: Overridden"
64
64
 
65
65
 
66
66
  def test_apply_format_prefers_override_even_if_empty(monkeypatch: pytest.MonkeyPatch) -> None:
67
- monkeypatch.setitem(settings.prompt.context, "merge_request_title", "")
68
- context = PromptContextSchema(merge_request_title="Local Value")
69
- template = "Title: <<merge_request_title>>"
67
+ monkeypatch.setitem(settings.prompt.context, "review_title", "")
68
+ context = PromptContextSchema(review_title="Local Value")
69
+ template = "Title: <<review_title>>"
70
70
  result = context.apply_format(template)
71
71
  assert result == "Title: "
@@ -20,15 +20,16 @@ def patch_prompts(monkeypatch: pytest.MonkeyPatch) -> None:
20
20
 
21
21
  @pytest.fixture
22
22
  def dummy_context() -> PromptContextSchema:
23
+ """Builds a context object that reflects the new unified review schema."""
23
24
  return PromptContextSchema(
24
- merge_request_title="Fix login bug",
25
- merge_request_description="Some description",
26
- merge_request_author_name="Nikita",
27
- merge_request_author_username="nikita.filonov",
28
- merge_request_reviewers=["Alice", "Bob"],
29
- merge_request_reviewers_usernames=["alice", "bob"],
30
- merge_request_assignees=["Charlie"],
31
- merge_request_assignees_usernames=["charlie"],
25
+ review_title="Fix login bug",
26
+ review_description="Some description",
27
+ review_author_name="Nikita",
28
+ review_author_username="nikita.filonov",
29
+ review_reviewers=["Alice", "Bob"],
30
+ review_reviewers_usernames=["alice", "bob"],
31
+ review_assignees=["Charlie"],
32
+ review_assignees_usernames=["charlie"],
32
33
  source_branch="feature/login-fix",
33
34
  target_branch="main",
34
35
  labels=["bug", "critical"],
@@ -130,16 +131,17 @@ def test_build_system_summary_request_empty(
130
131
 
131
132
 
132
133
  def test_diff_placeholders_are_not_replaced(dummy_context: PromptContextSchema) -> None:
133
- diffs = [DiffFileSchema(file="x.py", diff='print("<<merge_request_title>>")')]
134
+ diffs = [DiffFileSchema(file="x.py", diff='print("<<review_title>>")')]
134
135
  result = PromptService.build_summary_request(diffs, dummy_context)
135
136
 
136
- assert "<<merge_request_title>>" in result
137
+ assert "<<review_title>>" in result
137
138
  assert "Fix login bug" not in result
138
139
 
139
140
 
140
141
  def test_prepare_prompt_basic_substitution(dummy_context: PromptContextSchema) -> None:
141
- prompts = ["Hello", "MR title: <<merge_request_title>>"]
142
+ prompts = ["Hello", "MR title: <<review_title>>"]
142
143
  result = PromptService.prepare_prompt(prompts, dummy_context)
144
+
143
145
  assert "Hello" in result
144
146
  assert "MR title: Fix login bug" in result
145
147
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xai-review
3
- Version: 0.18.0
3
+ Version: 0.19.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>
@@ -9,17 +9,26 @@ Project-URL: Issues, https://github.com/Nikita-Filonov/ai-review/issues
9
9
  Project-URL: Homepage, https://github.com/Nikita-Filonov/ai-review
10
10
  Project-URL: Repository, https://github.com/Nikita-Filonov/ai-review
11
11
  Keywords: ai,code review,llm,openai,claude,gemini
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Quality Assurance
17
+ Classifier: Topic :: Software Development :: Testing
18
+ Classifier: License :: OSI Approved :: Apache Software License
19
+ Classifier: Operating System :: OS Independent
12
20
  Requires-Python: >=3.11
13
21
  Description-Content-Type: text/markdown
14
22
  License-File: LICENSE
15
23
  Requires-Dist: typer
16
24
  Requires-Dist: httpx
17
25
  Requires-Dist: pyyaml
18
- Requires-Dist: pytest
19
26
  Requires-Dist: loguru
20
27
  Requires-Dist: aiofiles
21
28
  Requires-Dist: pydantic
22
29
  Requires-Dist: pydantic-settings
30
+ Provides-Extra: test
31
+ Requires-Dist: pytest; extra == "test"
23
32
  Dynamic: license-file
24
33
 
25
34
  # AI Review
@@ -76,10 +85,10 @@ mode:
76
85
  | Mode | Description | Live Example |
77
86
  |------------|----------------------------------------------------|-------------------------------------------------------------------------------|
78
87
  | 🧩 Inline | Adds line-by-line comments directly in the diff | [Try AI Review (inline)](https://github.com/Nikita-Filonov/ai-review/pull/4) |
79
- | 📄 Summary | Posts a concise summary review with key highlights | [Try AI Review (context)](https://github.com/Nikita-Filonov/ai-review/pull/5) |
80
- | 🧠 Context | Performs broader analysis across multiple files | [Try AI Review (summary)](https://github.com/Nikita-Filonov/ai-review/pull/6) |
88
+ | 🧠 Context | Performs broader analysis across multiple files | [Try AI Review (context)](https://github.com/Nikita-Filonov/ai-review/pull/5) |
89
+ | 📄 Summary | Posts a concise summary review with key highlights | [Try AI Review (summary)](https://github.com/Nikita-Filonov/ai-review/pull/6) |
81
90
 
82
- Each review was generated automatically via GitHub Actions using the corresponding mode:
91
+ 👉 Each review was generated automatically via GitHub Actions using the corresponding mode:
83
92
 
84
93
  ```bash
85
94
  ai-review run-inline
@@ -97,12 +106,18 @@ Install via **pip**:
97
106
  pip install xai-review
98
107
  ```
99
108
 
109
+ 📦 Available on [PyPI](https://pypi.org/project/xai-review/)
110
+
111
+ ---
112
+
100
113
  Or run directly via Docker:
101
114
 
102
115
  ```bash
103
116
  docker run --rm -v $(pwd):/app nikitafilonov/ai-review:latest run-summary
104
117
  ```
105
118
 
119
+ 🐳 Pull from [DockerHub](https://hub.docker.com/r/nikitafilonov/ai-review)
120
+
106
121
  👉 Before running, create a basic configuration file [.ai-review.yaml](./docs/configs/.ai-review.yaml) in the root of
107
122
  your project:
108
123
 
@@ -192,7 +207,7 @@ jobs:
192
207
  runs-on: ubuntu-latest
193
208
  steps:
194
209
  - uses: actions/checkout@v4
195
- - uses: Nikita-Filonov/ai-review@v0.18.0
210
+ - uses: Nikita-Filonov/ai-review@v0.19.0
196
211
  with:
197
212
  review-command: ${{ inputs.review-command }}
198
213
  env:
@@ -17,20 +17,20 @@ ai_review/clients/gemini/schema.py,sha256=5oVvbI-h_sw8bFreS4JUmMj-aXa_frvxK3H8sg
17
17
  ai_review/clients/github/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  ai_review/clients/github/client.py,sha256=4uZsnMY-OZ9BNzMLqEHG80jgpyQdd61ePNdVuwGMcrI,1134
19
19
  ai_review/clients/github/pr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- ai_review/clients/github/pr/client.py,sha256=Trv5MwmOOi5gAM8KHknbpf8NWNZ-Tnag-bi_74KIdu0,4678
20
+ ai_review/clients/github/pr/client.py,sha256=sANW1K2MomgHzH4beqkkfHaEY2sRxiAVCDXjspOmcHA,5402
21
21
  ai_review/clients/github/pr/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  ai_review/clients/github/pr/schema/comments.py,sha256=K9KQ9TmWv9Hjw8uTrCPkzyAVbFohjCCf_rww6Ucj3wM,650
23
23
  ai_review/clients/github/pr/schema/files.py,sha256=mLHg1CfXUKCdQf5YUtuJ8n6xOROKoAjiJY5PL70kP-w,269
24
- ai_review/clients/github/pr/schema/pull_request.py,sha256=sdSvPgBkspW2DVO9GIyiqdhTngaVFFpYMCgcc5kFf8I,573
24
+ ai_review/clients/github/pr/schema/pull_request.py,sha256=EwOworYQY4kCmL6QFKEXK7r2fpINK8o-4-FEy9-nTpg,688
25
25
  ai_review/clients/github/pr/schema/reviews.py,sha256=v99DLYT5LOAcc18PATIse1mld8J0wKEAaTzUKI70s0c,288
26
26
  ai_review/clients/gitlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  ai_review/clients/gitlab/client.py,sha256=acMflkHGp8mv0TVLdZ1gmdXkWQPcq609QjmkYWjEmys,1136
28
28
  ai_review/clients/gitlab/mr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
- ai_review/clients/gitlab/mr/client.py,sha256=-4KHBie8NlHzX5LXdV9c9aL7UbHxuQ5XsDq701U-6q8,3844
29
+ ai_review/clients/gitlab/mr/client.py,sha256=IeLq4-aL6k9-4W_-bhCwsujKSUvT68oBB6YlgSsS_VQ,4522
30
30
  ai_review/clients/gitlab/mr/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- ai_review/clients/gitlab/mr/schema/changes.py,sha256=jmEgiLE07TNVU7nBe4hkP4xd1VOfpPYKF_7jBdalvq0,754
32
- ai_review/clients/gitlab/mr/schema/comments.py,sha256=F_ehfaZDsDPfJJH9HK1BTC3GAKZhm0Nkb15XbAgd1pA,382
33
- ai_review/clients/gitlab/mr/schema/discussions.py,sha256=b5Ro_W4dp1_JLEejRz05gYFwiNeoGk-8niz1z27rSIk,710
31
+ ai_review/clients/gitlab/mr/schema/changes.py,sha256=ZqSPb8zO0z_V8cEjxoTqnwbjRLxo6OTV4LeQEAg91cU,835
32
+ ai_review/clients/gitlab/mr/schema/discussions.py,sha256=DZqZTpLErmwSEsYSp6-XeBUo1kmFj3q6Zn_f3b6N9kk,718
33
+ ai_review/clients/gitlab/mr/schema/notes.py,sha256=Vm-OZ9z34h57FtzazSQSZ5ZFnq6Sw568zG3R99TQTzI,358
34
34
  ai_review/clients/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  ai_review/clients/openai/client.py,sha256=N07ZRnbptOtab8imMUZbGL-kRoOwIZmNYbHySumD3IU,1707
36
36
  ai_review/clients/openai/schema.py,sha256=glxwMtBrDA6W0BQgH-ruKe0bKH3Ps1P-Y1-2jGdqaUM,764
@@ -97,7 +97,7 @@ ai_review/services/git/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
97
97
  ai_review/services/git/service.py,sha256=_RMwgcllDoSLUzl84JML38fWkr7swnkUr6MJ46hSkWw,1282
98
98
  ai_review/services/git/types.py,sha256=QTOCTmR-Rt3sUjzZQHu2PGo_6un5gvNupifAa84wON4,413
99
99
  ai_review/services/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
- ai_review/services/llm/factory.py,sha256=QZn4jCOFAJX3pQb254e76mkKw156zA9KByTrKeMv41g,721
100
+ ai_review/services/llm/factory.py,sha256=fOyxSznQFdVuJQVilqtc0puWNoHCxZ5cdwtxRVfqTHw,725
101
101
  ai_review/services/llm/types.py,sha256=zMcMFQ7F9Zcgc9JqwdHCdlTpGOA0HuWyGgoySsUh4_o,345
102
102
  ai_review/services/llm/claude/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
103
  ai_review/services/llm/claude/client.py,sha256=w6CvfDYEAKyi_r88M-z_jCvMIJF3SjhQjV1EHMvNh24,1066
@@ -106,12 +106,12 @@ ai_review/services/llm/gemini/client.py,sha256=7FAOlTi8rV6b8g8aWcg-0LIP77AtbAoPt
106
106
  ai_review/services/llm/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
107
  ai_review/services/llm/openai/client.py,sha256=WhMXNfH_G1NTlFkdRK5sgYvrCIE5ZQNfPhdYx49IXFk,1143
108
108
  ai_review/services/prompt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
- ai_review/services/prompt/adapter.py,sha256=humGHLRVBu0JspeULgYHCs782BAy4YYKSf5yaG8aF24,1003
110
- ai_review/services/prompt/schema.py,sha256=erAecUYzOWyZfixt-pjmPSnvcMDh5DajMd1b7_SPm_o,2052
109
+ ai_review/services/prompt/adapter.py,sha256=YgD8Cf73pwPOnKxq9QSlbYr8wySfDxJE_3fIrRWfkUo,984
110
+ ai_review/services/prompt/schema.py,sha256=x-1KRFOK3HdZozXJNPh-Bp_JrZ2AIdAPD44lhWG5g9k,1863
111
111
  ai_review/services/prompt/service.py,sha256=D1PR2HC4cgrEND6mAhU5EPRAtp4mgEkOLEyD51WBc0g,2129
112
112
  ai_review/services/prompt/tools.py,sha256=-gS74mVM3OZPKWQkY9_QfStkfxaUfssDbJ3Bdi4AQ74,636
113
113
  ai_review/services/review/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
- ai_review/services/review/service.py,sha256=8YhRFqhZAk2pAnkDaytKSCENlOeOti1brAJq3R9tVMY,8394
114
+ ai_review/services/review/service.py,sha256=sYJQ02bKKQ926FpSmsHeeKfmRM1oMIqwjOFdKY88mws,8532
115
115
  ai_review/services/review/inline/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
116
  ai_review/services/review/inline/schema.py,sha256=ry8sJdTgusQvFW51neRiapzgzVGwswwJzdYhaV3hbT0,1545
117
117
  ai_review/services/review/inline/service.py,sha256=34I3eK2Ra1l3aUvSYfO-Wx9aPfcgCWqTgjdoGHlNO08,2135
@@ -121,12 +121,12 @@ ai_review/services/review/summary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
121
121
  ai_review/services/review/summary/schema.py,sha256=GipVNWrEKtgZPkytNSrXwzvX9Zq8Pv2wxjXhfJq4D3g,364
122
122
  ai_review/services/review/summary/service.py,sha256=GB7-l4UyjZfUe6yP_8Q-SD1_uDKHM0W-CZJVMiEL8S0,449
123
123
  ai_review/services/vcs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
124
- ai_review/services/vcs/factory.py,sha256=na1AOXgL9oUHqGIdRwT73BofxnkXEFnDr7fL3Sk_hkw,586
125
- ai_review/services/vcs/types.py,sha256=o3CJ8bZJ8unB9AKSpS66NwPVkFkweV4R02nCYsNqCko,1270
124
+ ai_review/services/vcs/factory.py,sha256=PSjBUClHRw0_38XmBYZBGIs281NMHX-vVzBwhnEn9uo,590
125
+ ai_review/services/vcs/types.py,sha256=ErKrALHyn7qdIg1_wZ5Af-A60HUotKHunZhIvC2GRKM,1990
126
126
  ai_review/services/vcs/github/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
- ai_review/services/vcs/github/client.py,sha256=ikW2YikslQm54U8e3yVfL3LizFmYbBzYa_FqkuDOajc,5634
127
+ ai_review/services/vcs/github/client.py,sha256=Pv18vGSjcXoapTsiLqKoEUZlv2ua7pp01IBRgxpAmM8,6301
128
128
  ai_review/services/vcs/gitlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
- ai_review/services/vcs/gitlab/client.py,sha256=-ZZFFlB7vv2DgEYAU016FP4CcYO8hp5LY1E2xokuCmU,6140
129
+ ai_review/services/vcs/gitlab/client.py,sha256=y1X99RVgA1KRcr_dW8gNaHuoSqxK1-30hzeIZA4HkhA,6678
130
130
  ai_review/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
131
131
  ai_review/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
132
132
  ai_review/tests/fixtures/git.py,sha256=CwMYAQNVEeHp8OCnFWh2fBKkBkyorWJtN2eDFqpYRbQ,1038
@@ -156,13 +156,17 @@ ai_review/tests/suites/libs/diff/test_tools.py,sha256=XkHJZ-b5veFz5oLKO09P7npaLN
156
156
  ai_review/tests/suites/libs/template/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
157
157
  ai_review/tests/suites/libs/template/test_render.py,sha256=n-ss5bd_hwc-RzYmqWmFM6KSlP1zLSnlsW1Yki12Bpw,1890
158
158
  ai_review/tests/suites/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
159
+ ai_review/tests/suites/services/cost/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
160
+ ai_review/tests/suites/services/cost/test_schema.py,sha256=AI3Wg1sR6nzLpkEqJGDu6nDYwiwzbbghsxhRNwRsUFA,3044
161
+ ai_review/tests/suites/services/cost/test_service.py,sha256=fMW4Tg6BRMXKcqOO7MmSqJc1mpuguvFSl0GjS93m7u8,3253
159
162
  ai_review/tests/suites/services/diff/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
160
163
  ai_review/tests/suites/services/diff/test_renderers.py,sha256=IKOpsGedONNW8ZfYTAk0Vq0hfFi7L6TpWs8vVVQroj0,6273
161
164
  ai_review/tests/suites/services/diff/test_service.py,sha256=iFkGX9Vj2X44JU3eFsoHsg9o9353eKX-QCv_J9KxfzU,3162
162
165
  ai_review/tests/suites/services/diff/test_tools.py,sha256=HBQ3eCn-kLeb_k5iTgyr09x0VwLYSegSbxm0Qk9ZrCc,3543
163
166
  ai_review/tests/suites/services/prompt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
164
- ai_review/tests/suites/services/prompt/test_schema.py,sha256=XkJk4N9ovgod7G3i6oZwRBjpd71sv0vtVDJhSOfIwGA,2660
165
- ai_review/tests/suites/services/prompt/test_service.py,sha256=plJ8xDnBifCrLtHJO00cdl11U1EsqSw7lBrEGxu0AIw,6752
167
+ ai_review/tests/suites/services/prompt/test_adapter.py,sha256=9KZOFQmZUs3l_cW7Q5LIMPs4i4J-gOCQ6VrlDPR0ImU,2156
168
+ ai_review/tests/suites/services/prompt/test_schema.py,sha256=DQyv5gUJ2VkxaD9wiKLS18ECopvvdKvF4sg3MTGcKs8,2547
169
+ ai_review/tests/suites/services/prompt/test_service.py,sha256=WXYKwDHMmWD6ew1awiEzmoxEJtQBqxvOgiyK8Ii9Mhw,6755
166
170
  ai_review/tests/suites/services/prompt/test_tools.py,sha256=_yNZoBATvPU5enWNIopbjY8lVVjfaB_46kNIKODhCW4,1981
167
171
  ai_review/tests/suites/services/review/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
168
172
  ai_review/tests/suites/services/review/inline/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -173,9 +177,9 @@ ai_review/tests/suites/services/review/policy/test_service.py,sha256=kRWT550OjWY
173
177
  ai_review/tests/suites/services/review/summary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
174
178
  ai_review/tests/suites/services/review/summary/test_schema.py,sha256=xSoydvABZldHaVDa0OBFvYrj8wMuZqUDN3MO-XdvxOI,819
175
179
  ai_review/tests/suites/services/review/summary/test_service.py,sha256=8UMvi_NL9frm280vD6Q1NCDrdI7K8YbXzoViIus-I2g,541
176
- xai_review-0.18.0.dist-info/licenses/LICENSE,sha256=p-v8m7Kmz4KKc7PcvsGiGEmCw9AiSXY4_ylOPy_u--Y,11343
177
- xai_review-0.18.0.dist-info/METADATA,sha256=OKCfkhU0YA7NGJjjNbMc066V1-5bmrv2ElgQsyNGoG4,9799
178
- xai_review-0.18.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
179
- xai_review-0.18.0.dist-info/entry_points.txt,sha256=JyC5URanMi5io5P_PXQf7H_I1OGIpk5cZQhaPQ0g4Zs,53
180
- xai_review-0.18.0.dist-info/top_level.txt,sha256=sTsZbfzLoqvRZKdKa-BcxWvjlHdrpbeJ6DrGY0EuR0E,10
181
- xai_review-0.18.0.dist-info/RECORD,,
180
+ xai_review-0.19.0.dist-info/licenses/LICENSE,sha256=p-v8m7Kmz4KKc7PcvsGiGEmCw9AiSXY4_ylOPy_u--Y,11343
181
+ xai_review-0.19.0.dist-info/METADATA,sha256=iaEYvCCq2eaoDSteH4tuHdBbq4j5Id_dRGZboXbnyPw,10409
182
+ xai_review-0.19.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
183
+ xai_review-0.19.0.dist-info/entry_points.txt,sha256=JyC5URanMi5io5P_PXQf7H_I1OGIpk5cZQhaPQ0g4Zs,53
184
+ xai_review-0.19.0.dist-info/top_level.txt,sha256=sTsZbfzLoqvRZKdKa-BcxWvjlHdrpbeJ6DrGY0EuR0E,10
185
+ xai_review-0.19.0.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- from pydantic import BaseModel, RootModel
2
-
3
-
4
- class GitLabMRCommentSchema(BaseModel):
5
- id: int
6
- body: str
7
-
8
-
9
- class GitLabGetMRCommentsResponseSchema(RootModel[list[GitLabMRCommentSchema]]):
10
- root: list[GitLabMRCommentSchema]
11
-
12
-
13
- class GitLabCreateMRCommentRequestSchema(BaseModel):
14
- body: str
15
-
16
-
17
- class GitLabCreateMRCommentResponseSchema(BaseModel):
18
- id: int
19
- body: str