xai-review 0.26.0__py3-none-any.whl → 0.27.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.
- ai_review/clients/bitbucket/pr/client.py +45 -8
- ai_review/clients/bitbucket/pr/schema/comments.py +7 -2
- ai_review/clients/bitbucket/pr/schema/files.py +8 -3
- ai_review/clients/bitbucket/tools.py +6 -0
- ai_review/clients/github/pr/client.py +66 -12
- ai_review/clients/github/pr/schema/comments.py +2 -1
- ai_review/clients/github/pr/schema/files.py +2 -1
- ai_review/clients/github/pr/schema/reviews.py +2 -1
- ai_review/clients/github/tools.py +6 -0
- ai_review/clients/gitlab/mr/client.py +35 -6
- ai_review/clients/gitlab/mr/schema/discussions.py +2 -1
- ai_review/clients/gitlab/mr/schema/notes.py +2 -1
- ai_review/clients/gitlab/tools.py +5 -0
- ai_review/libs/config/vcs/base.py +2 -0
- ai_review/libs/config/vcs/pagination.py +6 -0
- ai_review/libs/http/paginate.py +43 -0
- ai_review/tests/suites/clients/bitbucket/__init__.py +0 -0
- ai_review/tests/suites/clients/bitbucket/test_client.py +14 -0
- ai_review/tests/suites/clients/bitbucket/test_tools.py +31 -0
- ai_review/tests/suites/clients/github/test_tools.py +31 -0
- ai_review/tests/suites/clients/gitlab/test_tools.py +26 -0
- ai_review/tests/suites/libs/http/__init__.py +0 -0
- ai_review/tests/suites/libs/http/test_paginate.py +95 -0
- {xai_review-0.26.0.dist-info → xai_review-0.27.0.dist-info}/METADATA +2 -2
- {xai_review-0.26.0.dist-info → xai_review-0.27.0.dist-info}/RECORD +29 -17
- {xai_review-0.26.0.dist-info → xai_review-0.27.0.dist-info}/WHEEL +0 -0
- {xai_review-0.26.0.dist-info → xai_review-0.27.0.dist-info}/entry_points.txt +0 -0
- {xai_review-0.26.0.dist-info → xai_review-0.27.0.dist-info}/licenses/LICENSE +0 -0
- {xai_review-0.26.0.dist-info → xai_review-0.27.0.dist-info}/top_level.txt +0 -0
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
from httpx import Response, QueryParams
|
|
2
2
|
|
|
3
3
|
from ai_review.clients.bitbucket.pr.schema.comments import (
|
|
4
|
+
BitbucketPRCommentSchema,
|
|
4
5
|
BitbucketGetPRCommentsQuerySchema,
|
|
5
6
|
BitbucketGetPRCommentsResponseSchema,
|
|
6
7
|
BitbucketCreatePRCommentRequestSchema,
|
|
7
8
|
BitbucketCreatePRCommentResponseSchema,
|
|
8
9
|
)
|
|
9
10
|
from ai_review.clients.bitbucket.pr.schema.files import (
|
|
11
|
+
BitbucketPRFileSchema,
|
|
10
12
|
BitbucketGetPRFilesQuerySchema,
|
|
11
13
|
BitbucketGetPRFilesResponseSchema,
|
|
12
14
|
)
|
|
13
15
|
from ai_review.clients.bitbucket.pr.schema.pull_request import BitbucketGetPRResponseSchema
|
|
14
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
|
|
15
19
|
from ai_review.libs.http.client import HTTPClient
|
|
16
20
|
from ai_review.libs.http.handlers import handle_http_error, HTTPClientError
|
|
21
|
+
from ai_review.libs.http.paginate import paginate
|
|
17
22
|
|
|
18
23
|
|
|
19
24
|
class BitbucketPullRequestsHTTPClientError(HTTPClientError):
|
|
@@ -35,7 +40,7 @@ class BitbucketPullRequestsHTTPClient(HTTPClient, BitbucketPullRequestsHTTPClien
|
|
|
35
40
|
) -> Response:
|
|
36
41
|
return await self.get(
|
|
37
42
|
f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/diffstat",
|
|
38
|
-
query=QueryParams(**query.model_dump()),
|
|
43
|
+
query=QueryParams(**query.model_dump(by_alias=True)),
|
|
39
44
|
)
|
|
40
45
|
|
|
41
46
|
@handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
|
|
@@ -48,7 +53,7 @@ class BitbucketPullRequestsHTTPClient(HTTPClient, BitbucketPullRequestsHTTPClien
|
|
|
48
53
|
) -> Response:
|
|
49
54
|
return await self.get(
|
|
50
55
|
f"/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/comments",
|
|
51
|
-
query=QueryParams(**query.model_dump()),
|
|
56
|
+
query=QueryParams(**query.model_dump(by_alias=True)),
|
|
52
57
|
)
|
|
53
58
|
|
|
54
59
|
@handle_http_error(client="BitbucketPullRequestsHTTPClient", exception=BitbucketPullRequestsHTTPClientError)
|
|
@@ -79,9 +84,25 @@ class BitbucketPullRequestsHTTPClient(HTTPClient, BitbucketPullRequestsHTTPClien
|
|
|
79
84
|
repo_slug: str,
|
|
80
85
|
pull_request_id: str
|
|
81
86
|
) -> BitbucketGetPRFilesResponseSchema:
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
+
)
|
|
85
106
|
|
|
86
107
|
async def get_comments(
|
|
87
108
|
self,
|
|
@@ -89,9 +110,25 @@ class BitbucketPullRequestsHTTPClient(HTTPClient, BitbucketPullRequestsHTTPClien
|
|
|
89
110
|
repo_slug: str,
|
|
90
111
|
pull_request_id: str
|
|
91
112
|
) -> BitbucketGetPRCommentsResponseSchema:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
+
)
|
|
95
132
|
|
|
96
133
|
async def create_comment(
|
|
97
134
|
self,
|
|
@@ -22,15 +22,20 @@ class BitbucketPRCommentSchema(BaseModel):
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class BitbucketGetPRCommentsQuerySchema(BaseModel):
|
|
25
|
-
|
|
25
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
26
|
+
|
|
27
|
+
page: int = 1
|
|
28
|
+
page_len: int = Field(alias="pagelen", default=100)
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
class BitbucketGetPRCommentsResponseSchema(BaseModel):
|
|
32
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
33
|
+
|
|
29
34
|
size: int
|
|
30
35
|
page: int | None = None
|
|
31
36
|
next: str | None = None
|
|
32
37
|
values: list[BitbucketPRCommentSchema]
|
|
33
|
-
|
|
38
|
+
page_len: int = Field(alias="pagelen")
|
|
34
39
|
|
|
35
40
|
|
|
36
41
|
class BitbucketCreatePRCommentRequestSchema(BaseModel):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from pydantic import BaseModel
|
|
1
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class BitbucketPRFilePathSchema(BaseModel):
|
|
@@ -14,12 +14,17 @@ class BitbucketPRFileSchema(BaseModel):
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class BitbucketGetPRFilesQuerySchema(BaseModel):
|
|
17
|
-
|
|
17
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
18
|
+
|
|
19
|
+
page: int = 1
|
|
20
|
+
page_len: int = Field(alias="pagelen", default=100)
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
class BitbucketGetPRFilesResponseSchema(BaseModel):
|
|
24
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
25
|
+
|
|
21
26
|
size: int
|
|
22
27
|
page: int | None = None
|
|
23
28
|
next: str | None = None
|
|
24
29
|
values: list[BitbucketPRFileSchema]
|
|
25
|
-
|
|
30
|
+
page_len: int = Field(alias="pagelen")
|
|
@@ -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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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,
|
|
@@ -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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
21
|
+
page: int = 1
|
|
22
|
+
per_page: int = 100
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class GitLabGetMRDiscussionsResponseSchema(RootModel[list[GitLabDiscussionSchema]]):
|
|
@@ -5,11 +5,13 @@ from pydantic import BaseModel, Field
|
|
|
5
5
|
from ai_review.libs.config.vcs.bitbucket import BitbucketPipelineConfig, BitbucketHTTPClientConfig
|
|
6
6
|
from ai_review.libs.config.vcs.github import GitHubPipelineConfig, GitHubHTTPClientConfig
|
|
7
7
|
from ai_review.libs.config.vcs.gitlab import GitLabPipelineConfig, GitLabHTTPClientConfig
|
|
8
|
+
from ai_review.libs.config.vcs.pagination import VCSPaginationConfig
|
|
8
9
|
from ai_review.libs.constants.vcs_provider import VCSProvider
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class VCSConfigBase(BaseModel):
|
|
12
13
|
provider: VCSProvider
|
|
14
|
+
pagination: VCSPaginationConfig = VCSPaginationConfig()
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class GitLabVCSConfig(VCSConfigBase):
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from typing import Awaitable, Callable, TypeVar
|
|
2
|
+
|
|
3
|
+
from httpx import Response
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from ai_review.libs.logger import get_logger
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T", bound=BaseModel)
|
|
9
|
+
|
|
10
|
+
logger = get_logger("PAGINATE")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def paginate(
|
|
14
|
+
fetch_page: Callable[[int], Awaitable[Response]],
|
|
15
|
+
extract_items: Callable[[Response], list[T]],
|
|
16
|
+
has_next_page: Callable[[Response], bool],
|
|
17
|
+
max_pages: int | None = None,
|
|
18
|
+
) -> list[T]:
|
|
19
|
+
page = 1
|
|
20
|
+
items: list[T] = []
|
|
21
|
+
|
|
22
|
+
while True:
|
|
23
|
+
response = await fetch_page(page)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
extracted = extract_items(response)
|
|
27
|
+
except Exception as error:
|
|
28
|
+
logger.error(f"Failed to extract items on {page=}")
|
|
29
|
+
raise RuntimeError(f"Failed to extract items on {page=}") from error
|
|
30
|
+
|
|
31
|
+
logger.debug(f"Page {page}: extracted {len(extracted)} items (total={len(items) + len(extracted)})")
|
|
32
|
+
items.extend(extracted)
|
|
33
|
+
|
|
34
|
+
if not has_next_page(response):
|
|
35
|
+
logger.debug(f"Pagination finished after {page} page(s), total items={len(items)}")
|
|
36
|
+
break
|
|
37
|
+
|
|
38
|
+
page += 1
|
|
39
|
+
if max_pages and (page > max_pages):
|
|
40
|
+
logger.error(f"Pagination exceeded {max_pages=}")
|
|
41
|
+
raise RuntimeError(f"Pagination exceeded {max_pages=}")
|
|
42
|
+
|
|
43
|
+
return items
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from httpx import AsyncClient
|
|
3
|
+
|
|
4
|
+
from ai_review.clients.bitbucket.client import get_bitbucket_http_client, BitbucketHTTPClient
|
|
5
|
+
from ai_review.clients.bitbucket.pr.client import BitbucketPullRequestsHTTPClient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.usefixtures("bitbucket_http_client_config")
|
|
9
|
+
def test_get_bitbucket_http_client_builds_ok():
|
|
10
|
+
bitbucket_http_client = get_bitbucket_http_client()
|
|
11
|
+
|
|
12
|
+
assert isinstance(bitbucket_http_client, BitbucketHTTPClient)
|
|
13
|
+
assert isinstance(bitbucket_http_client.pr, BitbucketPullRequestsHTTPClient)
|
|
14
|
+
assert isinstance(bitbucket_http_client.pr.client, AsyncClient)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from httpx import Response, Request
|
|
2
|
+
|
|
3
|
+
from ai_review.clients.bitbucket.tools import bitbucket_has_next_page
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def make_response(data: dict) -> Response:
|
|
7
|
+
return Response(
|
|
8
|
+
json=data,
|
|
9
|
+
request=Request("GET", "http://bitbucket.test"),
|
|
10
|
+
status_code=200,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_bitbucket_has_next_page_true():
|
|
15
|
+
resp = make_response({"next": "https://api.bitbucket.org/2.0/repositories/test/repo?page=2"})
|
|
16
|
+
assert bitbucket_has_next_page(resp) is True
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_bitbucket_has_next_page_false_none():
|
|
20
|
+
resp = make_response({"next": None})
|
|
21
|
+
assert bitbucket_has_next_page(resp) is False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_bitbucket_has_next_page_false_missing():
|
|
25
|
+
resp = make_response({})
|
|
26
|
+
assert bitbucket_has_next_page(resp) is False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_bitbucket_has_next_page_false_empty_string():
|
|
30
|
+
resp = make_response({"next": ""})
|
|
31
|
+
assert bitbucket_has_next_page(resp) is False
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from httpx import Response, Request
|
|
2
|
+
|
|
3
|
+
from ai_review.clients.github.tools import github_has_next_page
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def make_response(headers: dict) -> Response:
|
|
7
|
+
return Response(
|
|
8
|
+
request=Request("GET", "http://test"),
|
|
9
|
+
headers=headers,
|
|
10
|
+
status_code=200,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_github_has_next_page_true():
|
|
15
|
+
response = make_response({
|
|
16
|
+
"Link": '<https://api.github.com/resource?page=2>; rel="next", '
|
|
17
|
+
'<https://api.github.com/resource?page=5>; rel="last"'
|
|
18
|
+
})
|
|
19
|
+
assert github_has_next_page(response) is True
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_github_has_next_page_false_no_next():
|
|
23
|
+
response = make_response({
|
|
24
|
+
"Link": '<https://api.github.com/resource?page=5>; rel="last"'
|
|
25
|
+
})
|
|
26
|
+
assert github_has_next_page(response) is False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_github_has_next_page_false_no_header():
|
|
30
|
+
resp = make_response({})
|
|
31
|
+
assert github_has_next_page(resp) is False
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from httpx import Response, Request
|
|
2
|
+
|
|
3
|
+
from ai_review.clients.gitlab.tools import gitlab_has_next_page
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def make_response(headers: dict) -> Response:
|
|
7
|
+
return Response(
|
|
8
|
+
request=Request("GET", "http://gitlab.test"),
|
|
9
|
+
headers=headers,
|
|
10
|
+
status_code=200,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_gitlab_has_next_page_true():
|
|
15
|
+
resp = make_response({"X-Next-Page": "2"})
|
|
16
|
+
assert gitlab_has_next_page(resp) is True
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_gitlab_has_next_page_false_empty():
|
|
20
|
+
resp = make_response({"X-Next-Page": ""})
|
|
21
|
+
assert gitlab_has_next_page(resp) is False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_gitlab_has_next_page_false_missing():
|
|
25
|
+
resp = make_response({})
|
|
26
|
+
assert gitlab_has_next_page(resp) is False
|
|
File without changes
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from httpx import Response, Request
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from ai_review.libs.http.paginate import paginate
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DummySchema(BaseModel):
|
|
9
|
+
value: int
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def make_response(data: dict) -> Response:
|
|
13
|
+
return Response(
|
|
14
|
+
json=data,
|
|
15
|
+
request=Request("GET", "http://test"),
|
|
16
|
+
status_code=200,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.mark.asyncio
|
|
21
|
+
async def test_single_page():
|
|
22
|
+
async def fetch_page(_: int) -> Response:
|
|
23
|
+
return make_response({"items": [1, 2, 3]})
|
|
24
|
+
|
|
25
|
+
def extract_items(response: Response) -> list[DummySchema]:
|
|
26
|
+
return [DummySchema(value=value) for value in response.json()["items"]]
|
|
27
|
+
|
|
28
|
+
def has_next_page(_: Response) -> bool:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
items = await paginate(fetch_page, extract_items, has_next_page)
|
|
32
|
+
assert len(items) == 3
|
|
33
|
+
assert [item.value for item in items] == [1, 2, 3]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.mark.asyncio
|
|
37
|
+
async def test_multiple_pages():
|
|
38
|
+
async def fetch_page(page: int) -> Response:
|
|
39
|
+
return make_response({"items": [page]})
|
|
40
|
+
|
|
41
|
+
def extract_items(response: Response):
|
|
42
|
+
return [DummySchema(value=value) for value in response.json()["items"]]
|
|
43
|
+
|
|
44
|
+
def has_next_page(response: Response) -> bool:
|
|
45
|
+
return response.json()["items"][0] < 3
|
|
46
|
+
|
|
47
|
+
items = await paginate(fetch_page, extract_items, has_next_page)
|
|
48
|
+
assert [item.value for item in items] == [1, 2, 3]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest.mark.asyncio
|
|
52
|
+
async def test_extract_items_error():
|
|
53
|
+
async def fetch_page(_: int) -> Response:
|
|
54
|
+
return make_response({"items": [1]})
|
|
55
|
+
|
|
56
|
+
def extract_items(_: Response):
|
|
57
|
+
raise ValueError("bad json")
|
|
58
|
+
|
|
59
|
+
def has_next_page(_: Response) -> bool:
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
with pytest.raises(RuntimeError) as exc:
|
|
63
|
+
await paginate(fetch_page, extract_items, has_next_page)
|
|
64
|
+
assert "Failed to extract items" in str(exc.value)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@pytest.mark.asyncio
|
|
68
|
+
async def test_max_pages_exceeded():
|
|
69
|
+
async def fetch_page(page: int) -> Response:
|
|
70
|
+
return make_response({"items": [page]})
|
|
71
|
+
|
|
72
|
+
def extract_items(response: Response):
|
|
73
|
+
return [DummySchema(value=value) for value in response.json()["items"]]
|
|
74
|
+
|
|
75
|
+
def has_next_page(_: Response) -> bool:
|
|
76
|
+
return True
|
|
77
|
+
|
|
78
|
+
with pytest.raises(RuntimeError) as exc:
|
|
79
|
+
await paginate(fetch_page, extract_items, has_next_page, max_pages=2)
|
|
80
|
+
assert "Pagination exceeded" in str(exc.value)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@pytest.mark.asyncio
|
|
84
|
+
async def test_empty_items():
|
|
85
|
+
async def fetch_page(_: int) -> Response:
|
|
86
|
+
return make_response({"items": []})
|
|
87
|
+
|
|
88
|
+
def extract_items(_: Response):
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
def has_next_page(_: Response) -> bool:
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
result = await paginate(fetch_page, extract_items, has_next_page)
|
|
95
|
+
assert result == []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xai-review
|
|
3
|
-
Version: 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>
|
|
@@ -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.
|
|
212
|
+
- uses: Nikita-Filonov/ai-review@v0.27.0
|
|
213
213
|
with:
|
|
214
214
|
review-command: ${{ inputs.review-command }}
|
|
215
215
|
env:
|
|
@@ -10,12 +10,13 @@ ai_review/cli/commands/run_summary_review.py,sha256=NqjepGH5cbqczPzcuMEAxO4dI58F
|
|
|
10
10
|
ai_review/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
ai_review/clients/bitbucket/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
ai_review/clients/bitbucket/client.py,sha256=VaqaQ5USMPTOEeS5XPdr-RkMKsxUpJ2SBE6lcemkz-g,1174
|
|
13
|
+
ai_review/clients/bitbucket/tools.py,sha256=UGBCurb8MQECivWDJDNXr7ej7rwA5-_kXTT4zF8RXIQ,147
|
|
13
14
|
ai_review/clients/bitbucket/pr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
ai_review/clients/bitbucket/pr/client.py,sha256=
|
|
15
|
+
ai_review/clients/bitbucket/pr/client.py,sha256=YnA5GCw_qqrjraNH_LjtE7t7cCmrMTY9IrcxDT2uyZ0,5836
|
|
15
16
|
ai_review/clients/bitbucket/pr/types.py,sha256=ZICV4ghYChj1Jl9Nlwyw1_kwmGybX51GhGdGzkRaLCk,1296
|
|
16
17
|
ai_review/clients/bitbucket/pr/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
ai_review/clients/bitbucket/pr/schema/comments.py,sha256=
|
|
18
|
-
ai_review/clients/bitbucket/pr/schema/files.py,sha256=
|
|
18
|
+
ai_review/clients/bitbucket/pr/schema/comments.py,sha256=p3HQyi-M7DLXcD0RErVlqsnKhmo2JVqdA8ejVYF_Kzg,1337
|
|
19
|
+
ai_review/clients/bitbucket/pr/schema/files.py,sha256=qBDfIv370TDMKM8yoLlm1c-BTHNvEZRuhnBo18nkx9g,750
|
|
19
20
|
ai_review/clients/bitbucket/pr/schema/pull_request.py,sha256=buGULgaCkxCUFSdiw0XTwaSIYP_p1rAEuKXUyJ_Mzi8,863
|
|
20
21
|
ai_review/clients/claude/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
22
|
ai_review/clients/claude/client.py,sha256=uEadbBNBJnzjHDczbxXiiw1V1H1PdUWKu-Gn-eIDEmw,1890
|
|
@@ -27,23 +28,25 @@ ai_review/clients/gemini/schema.py,sha256=5oVvbI-h_sw8bFreS4JUmMj-aXa_frvxK3H8sg
|
|
|
27
28
|
ai_review/clients/gemini/types.py,sha256=D-P0THorrQ8yq5P-NKAC65zzhEYRa9HkiXTORG9QoIk,267
|
|
28
29
|
ai_review/clients/github/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
30
|
ai_review/clients/github/client.py,sha256=pprQcCYdrhRYtuqRsTFiCbj54Qb1Ll6_jmlm7AJg8pk,1149
|
|
31
|
+
ai_review/clients/github/tools.py,sha256=sD2eS_iNhzT4ZLgTRmO2uBLuId-fa2aSvbu6VFeKSlc,201
|
|
30
32
|
ai_review/clients/github/pr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
-
ai_review/clients/github/pr/client.py,sha256=
|
|
33
|
+
ai_review/clients/github/pr/client.py,sha256=HysL1u-WxLdhGKi-6HAx6fN-50XSk3yK55gZ9Nii72U,8851
|
|
32
34
|
ai_review/clients/github/pr/types.py,sha256=mI5Vtlxc25iymOTPkNl55IQygXd4ti3B1Ydpx3q1fps,1726
|
|
33
35
|
ai_review/clients/github/pr/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
-
ai_review/clients/github/pr/schema/comments.py,sha256=
|
|
35
|
-
ai_review/clients/github/pr/schema/files.py,sha256=
|
|
36
|
+
ai_review/clients/github/pr/schema/comments.py,sha256=dIlX6tYPseAtLquk-UIie7jxBxrCbmPbDyB8bC9OFPo,743
|
|
37
|
+
ai_review/clients/github/pr/schema/files.py,sha256=ARirokToZ1zqMWh2jpDtVIs4qw5rlL7xfoRDJBJF71o,359
|
|
36
38
|
ai_review/clients/github/pr/schema/pull_request.py,sha256=EwOworYQY4kCmL6QFKEXK7r2fpINK8o-4-FEy9-nTpg,688
|
|
37
|
-
ai_review/clients/github/pr/schema/reviews.py,sha256=
|
|
39
|
+
ai_review/clients/github/pr/schema/reviews.py,sha256=qRJpBu03CBwC_tPng9q9tkqT_u2WcdDELblAi-OzxWI,380
|
|
38
40
|
ai_review/clients/gitlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
41
|
ai_review/clients/gitlab/client.py,sha256=MIZed-V4JUzntkU9vJq7Fkp5VAjnzjvep_5QM5ni_Co,1151
|
|
42
|
+
ai_review/clients/gitlab/tools.py,sha256=ed8t2-dy2XtSRpYWhLxUobYvbBX_1zMZTPuzjbgPzTg,136
|
|
40
43
|
ai_review/clients/gitlab/mr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
-
ai_review/clients/gitlab/mr/client.py,sha256=
|
|
44
|
+
ai_review/clients/gitlab/mr/client.py,sha256=39EjS0bvvi8-kiBOkBa7N1q0wHb0qTgo_KvQise61js,6350
|
|
42
45
|
ai_review/clients/gitlab/mr/types.py,sha256=tZ3rDDkzJM8v4zPt4gTyKCL30htiPQE9F_kVR6K5JHA,1280
|
|
43
46
|
ai_review/clients/gitlab/mr/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
47
|
ai_review/clients/gitlab/mr/schema/changes.py,sha256=ZqSPb8zO0z_V8cEjxoTqnwbjRLxo6OTV4LeQEAg91cU,835
|
|
45
|
-
ai_review/clients/gitlab/mr/schema/discussions.py,sha256=
|
|
46
|
-
ai_review/clients/gitlab/mr/schema/notes.py,sha256=
|
|
48
|
+
ai_review/clients/gitlab/mr/schema/discussions.py,sha256=owaWWD5lMG9GXkT8kgHiU_-khYTWGHanbWgSKDiJknA,814
|
|
49
|
+
ai_review/clients/gitlab/mr/schema/notes.py,sha256=DX9juurMYKcaYobh4n4M49dvHtUiLw5IzZKsGdBMxZg,448
|
|
47
50
|
ai_review/clients/ollama/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
51
|
ai_review/clients/ollama/client.py,sha256=KoJ9J5_Vfpv5XNJREshE_gA46uo9J0Z3qVC7wJPEcX8,1720
|
|
49
52
|
ai_review/clients/ollama/schema.py,sha256=A6oKwkkEVrduyzMR_lhLnaLyvKXqlfsXjkMIF2eXaYw,1310
|
|
@@ -74,10 +77,11 @@ ai_review/libs/config/llm/meta.py,sha256=cEcAHOwy-mQBKo9_KJrQe0I7qppq6h99lSmoWX4
|
|
|
74
77
|
ai_review/libs/config/llm/ollama.py,sha256=M6aiPb5GvYvkiGcgHTsh9bOw5JsBLqmfSKoIbHCejrU,372
|
|
75
78
|
ai_review/libs/config/llm/openai.py,sha256=jGVL4gJ2wIacoKeK9Zc9LCgY95TxdeYOThdglVPErFU,262
|
|
76
79
|
ai_review/libs/config/vcs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
|
-
ai_review/libs/config/vcs/base.py,sha256=
|
|
80
|
+
ai_review/libs/config/vcs/base.py,sha256=B0kKc6-A3mmV7dvHpo47u-1yTvdUCvLj_g4oCBo_NyY,1214
|
|
78
81
|
ai_review/libs/config/vcs/bitbucket.py,sha256=on5sQaE57kM_zSmqdDUNrttVtTPGOzqLHM5s7eFN7DA,275
|
|
79
82
|
ai_review/libs/config/vcs/github.py,sha256=hk-kuDLd8wecqtEb8PSqF7Yy_pkihplJhi6nB6FZID4,256
|
|
80
83
|
ai_review/libs/config/vcs/gitlab.py,sha256=ecYfU158VgVlM6P5mgZn8FOqk3Xt60xx7gUqT5e22a4,252
|
|
84
|
+
ai_review/libs/config/vcs/pagination.py,sha256=S-XxWQYkIjhu3ffpHQ44d7UtRHH81fh9GaJ-xQXUUy4,175
|
|
81
85
|
ai_review/libs/constants/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
82
86
|
ai_review/libs/constants/llm_provider.py,sha256=k7GzctIZ-TDsRlhTPbpGYgym_CO2YKVFp_oXG9dTBW0,143
|
|
83
87
|
ai_review/libs/constants/vcs_provider.py,sha256=xJpRdJIdAf05iH2x2f362d1MuviOlPVP7In-JvDVotE,127
|
|
@@ -88,6 +92,7 @@ ai_review/libs/diff/tools.py,sha256=CZWRDlOW2YS-b8h9gv_uP1MG194_FLkKzcKTwwZHocI,
|
|
|
88
92
|
ai_review/libs/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
93
|
ai_review/libs/http/client.py,sha256=JONzbhJWJKFz8Jy6p9pzq2hAMKlyJ2_WkBksuAlqW7k,453
|
|
90
94
|
ai_review/libs/http/handlers.py,sha256=k1VvCIFjLzfH3qQ--aj4CZVgbU0oj78sYStMBrxo_Ek,1040
|
|
95
|
+
ai_review/libs/http/paginate.py,sha256=yAryDaUkQd-wojXOaak9HyicT-QZwx3L49AJlUEins4,1305
|
|
91
96
|
ai_review/libs/http/event_hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
92
97
|
ai_review/libs/http/event_hooks/base.py,sha256=cnSOOButTJYKeyb_OnGms1vXRfwfExP81L3ZfYWLufk,279
|
|
93
98
|
ai_review/libs/http/event_hooks/logger.py,sha256=8_omfl6q3JijaBBIgzvzb4SayjNEDW-oxyck_Ky8wnI,603
|
|
@@ -191,6 +196,9 @@ ai_review/tests/fixtures/services/review/inline.py,sha256=k4IW6oy5JHMo9Kv0H97DLl
|
|
|
191
196
|
ai_review/tests/fixtures/services/review/summary.py,sha256=Hkt8mq1ZmqMH5_mELrS1x0wtCoNPbBjOEQ9yIsMbRts,691
|
|
192
197
|
ai_review/tests/suites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
193
198
|
ai_review/tests/suites/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
199
|
+
ai_review/tests/suites/clients/bitbucket/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
200
|
+
ai_review/tests/suites/clients/bitbucket/test_client.py,sha256=1qEkznC01hkh6TPpzChY9glhK6OTX-pfnSF43KNBtrY,600
|
|
201
|
+
ai_review/tests/suites/clients/bitbucket/test_tools.py,sha256=naHq0Xy7uP8pYdWR8JVrET5JJpNb0Z2-TEPkGHh2fFI,887
|
|
194
202
|
ai_review/tests/suites/clients/claude/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
195
203
|
ai_review/tests/suites/clients/claude/test_client.py,sha256=jLGqK7lzYc8LjJO-3HjBtLqCdg-ubw-xZ4EvFeFGRhY,404
|
|
196
204
|
ai_review/tests/suites/clients/claude/test_schema.py,sha256=MUZXvEROgLNpUVHfCsH5D3ruJPQwTx0OgeT3_BRVjgI,1671
|
|
@@ -199,8 +207,10 @@ ai_review/tests/suites/clients/gemini/test_client.py,sha256=f2R7KisiENrzf8gaK26N
|
|
|
199
207
|
ai_review/tests/suites/clients/gemini/test_schema.py,sha256=88dU28m7sEWvGx6tqYl7if7weWYuVc8erlkFkKKI3bk,3109
|
|
200
208
|
ai_review/tests/suites/clients/github/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
201
209
|
ai_review/tests/suites/clients/github/test_client.py,sha256=BiuLKCHIk83U1szYEZkB-n3vvyPgj6tAI5EqxKiT-CY,558
|
|
210
|
+
ai_review/tests/suites/clients/github/test_tools.py,sha256=_RKMWNgfeynnpbiDebRLg-1Qz91Kyevf5drl4hCngzU,881
|
|
202
211
|
ai_review/tests/suites/clients/gitlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
203
212
|
ai_review/tests/suites/clients/gitlab/test_client.py,sha256=5QOkNvgm0XRKHh79FNIY9CTonAqYPXqCCxcxeiAHYCA,560
|
|
213
|
+
ai_review/tests/suites/clients/gitlab/test_tools.py,sha256=-gS_kvZwNBkvYeYyYPld78F4ZuZPrpNORWVbg2eq5wM,678
|
|
204
214
|
ai_review/tests/suites/clients/ollama/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
205
215
|
ai_review/tests/suites/clients/ollama/test_client.py,sha256=XZ8NAd1bS_ltTuYZPgqlutPRA6kbvH3_3SKTCbNBTgA,404
|
|
206
216
|
ai_review/tests/suites/clients/ollama/test_schema.py,sha256=A93wCmxwGdvudfbA97VCPYP3gT6u6EYMetAg5fgURRA,1836
|
|
@@ -217,6 +227,8 @@ ai_review/tests/suites/libs/diff/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
|
|
|
217
227
|
ai_review/tests/suites/libs/diff/test_models.py,sha256=RBFQ97LWhU8TlupxXkJ97ryAvJrSuOHLtT9biUBUMXg,3321
|
|
218
228
|
ai_review/tests/suites/libs/diff/test_parser.py,sha256=rvWEVGIdaLBlDAnSevjRY7I1Zikj12d5GOgMk9QyHQQ,3013
|
|
219
229
|
ai_review/tests/suites/libs/diff/test_tools.py,sha256=XkHJZ-b5veFz5oLKO09P7npaLN8lOzCnGR7e83Zv_mg,1953
|
|
230
|
+
ai_review/tests/suites/libs/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
231
|
+
ai_review/tests/suites/libs/http/test_paginate.py,sha256=9PCn1mUQypPId9l8RToBQnLBJBV_WEYxBh785bqqmMA,2694
|
|
220
232
|
ai_review/tests/suites/libs/template/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
221
233
|
ai_review/tests/suites/libs/template/test_render.py,sha256=n-ss5bd_hwc-RzYmqWmFM6KSlP1zLSnlsW1Yki12Bpw,1890
|
|
222
234
|
ai_review/tests/suites/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -262,9 +274,9 @@ ai_review/tests/suites/services/vcs/github/__init__.py,sha256=47DEQpj8HBSa-_TImW
|
|
|
262
274
|
ai_review/tests/suites/services/vcs/github/test_service.py,sha256=c2sjecm4qzqYXuO9j6j35NQyJzqDpnXIJImRTcpkyHo,4378
|
|
263
275
|
ai_review/tests/suites/services/vcs/gitlab/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
264
276
|
ai_review/tests/suites/services/vcs/gitlab/test_service.py,sha256=0dqgL5whzjcP-AQ4adP_12QfkYm_ZtdtMotmYm8Se7Y,4449
|
|
265
|
-
xai_review-0.
|
|
266
|
-
xai_review-0.
|
|
267
|
-
xai_review-0.
|
|
268
|
-
xai_review-0.
|
|
269
|
-
xai_review-0.
|
|
270
|
-
xai_review-0.
|
|
277
|
+
xai_review-0.27.0.dist-info/licenses/LICENSE,sha256=p-v8m7Kmz4KKc7PcvsGiGEmCw9AiSXY4_ylOPy_u--Y,11343
|
|
278
|
+
xai_review-0.27.0.dist-info/METADATA,sha256=5SnK3Ip4vzfwvS110fYNDSDYD4OzrmA5G1eUV2oGMbE,11150
|
|
279
|
+
xai_review-0.27.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
280
|
+
xai_review-0.27.0.dist-info/entry_points.txt,sha256=JyC5URanMi5io5P_PXQf7H_I1OGIpk5cZQhaPQ0g4Zs,53
|
|
281
|
+
xai_review-0.27.0.dist-info/top_level.txt,sha256=sTsZbfzLoqvRZKdKa-BcxWvjlHdrpbeJ6DrGY0EuR0E,10
|
|
282
|
+
xai_review-0.27.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|