xai-review 0.26.0__py3-none-any.whl → 0.28.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/cli/commands/run_inline_reply_review.py +7 -0
- ai_review/cli/commands/run_summary_reply_review.py +7 -0
- ai_review/cli/main.py +17 -0
- ai_review/clients/bitbucket/pr/client.py +45 -8
- ai_review/clients/bitbucket/pr/schema/comments.py +21 -2
- ai_review/clients/bitbucket/pr/schema/files.py +8 -3
- ai_review/clients/bitbucket/pr/schema/pull_request.py +1 -5
- ai_review/clients/bitbucket/pr/schema/user.py +7 -0
- ai_review/clients/bitbucket/tools.py +6 -0
- ai_review/clients/github/pr/client.py +98 -13
- ai_review/clients/github/pr/schema/comments.py +23 -1
- ai_review/clients/github/pr/schema/files.py +2 -1
- ai_review/clients/github/pr/schema/pull_request.py +1 -4
- ai_review/clients/github/pr/schema/reviews.py +2 -1
- ai_review/clients/github/pr/schema/user.py +6 -0
- ai_review/clients/github/pr/types.py +11 -1
- ai_review/clients/github/tools.py +6 -0
- ai_review/clients/gitlab/mr/client.py +67 -7
- ai_review/clients/gitlab/mr/schema/changes.py +1 -5
- ai_review/clients/gitlab/mr/schema/discussions.py +19 -8
- ai_review/clients/gitlab/mr/schema/notes.py +5 -1
- ai_review/clients/gitlab/mr/schema/user.py +7 -0
- ai_review/clients/gitlab/mr/types.py +16 -7
- ai_review/clients/gitlab/tools.py +5 -0
- ai_review/libs/config/prompt.py +96 -64
- ai_review/libs/config/review.py +2 -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/libs/llm/output_json_parser.py +60 -0
- ai_review/prompts/default_inline_reply.md +10 -0
- ai_review/prompts/default_summary_reply.md +14 -0
- ai_review/prompts/default_system_inline_reply.md +31 -0
- ai_review/prompts/default_system_summary_reply.md +13 -0
- ai_review/services/artifacts/schema.py +2 -2
- ai_review/services/hook/constants.py +14 -0
- ai_review/services/hook/service.py +95 -4
- ai_review/services/hook/types.py +18 -2
- ai_review/services/prompt/adapter.py +1 -1
- ai_review/services/prompt/service.py +49 -3
- ai_review/services/prompt/tools.py +21 -0
- ai_review/services/prompt/types.py +23 -0
- ai_review/services/review/gateway/comment.py +45 -6
- ai_review/services/review/gateway/llm.py +2 -1
- ai_review/services/review/gateway/types.py +50 -0
- ai_review/services/review/internal/inline/service.py +40 -0
- ai_review/services/review/internal/inline/types.py +8 -0
- ai_review/services/review/internal/inline_reply/schema.py +23 -0
- ai_review/services/review/internal/inline_reply/service.py +20 -0
- ai_review/services/review/internal/inline_reply/types.py +8 -0
- ai_review/services/review/{policy → internal/policy}/service.py +2 -1
- ai_review/services/review/internal/policy/types.py +15 -0
- ai_review/services/review/{summary → internal/summary}/service.py +2 -2
- ai_review/services/review/{summary → internal/summary}/types.py +1 -1
- ai_review/services/review/internal/summary_reply/__init__.py +0 -0
- ai_review/services/review/internal/summary_reply/schema.py +8 -0
- ai_review/services/review/internal/summary_reply/service.py +15 -0
- ai_review/services/review/internal/summary_reply/types.py +8 -0
- ai_review/services/review/runner/__init__.py +0 -0
- ai_review/services/review/runner/context.py +72 -0
- ai_review/services/review/runner/inline.py +80 -0
- ai_review/services/review/runner/inline_reply.py +80 -0
- ai_review/services/review/runner/summary.py +71 -0
- ai_review/services/review/runner/summary_reply.py +79 -0
- ai_review/services/review/runner/types.py +6 -0
- ai_review/services/review/service.py +78 -110
- ai_review/services/vcs/bitbucket/adapter.py +24 -0
- ai_review/services/vcs/bitbucket/client.py +107 -42
- ai_review/services/vcs/github/adapter.py +35 -0
- ai_review/services/vcs/github/client.py +105 -44
- ai_review/services/vcs/gitlab/adapter.py +26 -0
- ai_review/services/vcs/gitlab/client.py +91 -38
- ai_review/services/vcs/types.py +34 -0
- ai_review/tests/fixtures/clients/bitbucket.py +2 -2
- ai_review/tests/fixtures/clients/github.py +35 -6
- ai_review/tests/fixtures/clients/gitlab.py +42 -3
- ai_review/tests/fixtures/libs/__init__.py +0 -0
- ai_review/tests/fixtures/libs/llm/__init__.py +0 -0
- ai_review/tests/fixtures/libs/llm/output_json_parser.py +13 -0
- ai_review/tests/fixtures/services/hook.py +8 -0
- ai_review/tests/fixtures/services/llm.py +8 -5
- ai_review/tests/fixtures/services/prompt.py +70 -0
- ai_review/tests/fixtures/services/review/base.py +41 -0
- ai_review/tests/fixtures/services/review/gateway/__init__.py +0 -0
- ai_review/tests/fixtures/services/review/gateway/comment.py +98 -0
- ai_review/tests/fixtures/services/review/gateway/llm.py +17 -0
- ai_review/tests/fixtures/services/review/internal/__init__.py +0 -0
- ai_review/tests/fixtures/services/review/{inline.py → internal/inline.py} +8 -6
- ai_review/tests/fixtures/services/review/internal/inline_reply.py +25 -0
- ai_review/tests/fixtures/services/review/internal/policy.py +28 -0
- ai_review/tests/fixtures/services/review/internal/summary.py +21 -0
- ai_review/tests/fixtures/services/review/internal/summary_reply.py +19 -0
- ai_review/tests/fixtures/services/review/runner/__init__.py +0 -0
- ai_review/tests/fixtures/services/review/runner/context.py +50 -0
- ai_review/tests/fixtures/services/review/runner/inline.py +50 -0
- ai_review/tests/fixtures/services/review/runner/inline_reply.py +50 -0
- ai_review/tests/fixtures/services/review/runner/summary.py +50 -0
- ai_review/tests/fixtures/services/review/runner/summary_reply.py +50 -0
- ai_review/tests/fixtures/services/vcs.py +23 -0
- ai_review/tests/suites/cli/__init__.py +0 -0
- ai_review/tests/suites/cli/test_main.py +54 -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/config/test_prompt.py +108 -28
- ai_review/tests/suites/libs/http/__init__.py +0 -0
- ai_review/tests/suites/libs/http/test_paginate.py +95 -0
- ai_review/tests/suites/libs/llm/__init__.py +0 -0
- ai_review/tests/suites/libs/llm/test_output_json_parser.py +155 -0
- ai_review/tests/suites/services/hook/test_service.py +88 -4
- ai_review/tests/suites/services/prompt/test_adapter.py +3 -3
- ai_review/tests/suites/services/prompt/test_service.py +102 -58
- ai_review/tests/suites/services/prompt/test_tools.py +86 -1
- ai_review/tests/suites/services/review/gateway/__init__.py +0 -0
- ai_review/tests/suites/services/review/gateway/test_comment.py +253 -0
- ai_review/tests/suites/services/review/gateway/test_llm.py +82 -0
- ai_review/tests/suites/services/review/internal/__init__.py +0 -0
- ai_review/tests/suites/services/review/internal/inline/__init__.py +0 -0
- ai_review/tests/suites/services/review/{inline → internal/inline}/test_schema.py +1 -1
- ai_review/tests/suites/services/review/internal/inline/test_service.py +81 -0
- ai_review/tests/suites/services/review/internal/inline_reply/__init__.py +0 -0
- ai_review/tests/suites/services/review/internal/inline_reply/test_schema.py +57 -0
- ai_review/tests/suites/services/review/internal/inline_reply/test_service.py +72 -0
- ai_review/tests/suites/services/review/internal/policy/__init__.py +0 -0
- ai_review/tests/suites/services/review/{policy → internal/policy}/test_service.py +1 -1
- ai_review/tests/suites/services/review/internal/summary/__init__.py +0 -0
- ai_review/tests/suites/services/review/{summary → internal/summary}/test_schema.py +1 -1
- ai_review/tests/suites/services/review/{summary → internal/summary}/test_service.py +2 -2
- ai_review/tests/suites/services/review/internal/summary_reply/__init__.py +0 -0
- ai_review/tests/suites/services/review/internal/summary_reply/test_schema.py +19 -0
- ai_review/tests/suites/services/review/internal/summary_reply/test_service.py +21 -0
- ai_review/tests/suites/services/review/runner/__init__.py +0 -0
- ai_review/tests/suites/services/review/runner/test_context.py +89 -0
- ai_review/tests/suites/services/review/runner/test_inline.py +100 -0
- ai_review/tests/suites/services/review/runner/test_inline_reply.py +109 -0
- ai_review/tests/suites/services/review/runner/test_summary.py +87 -0
- ai_review/tests/suites/services/review/runner/test_summary_reply.py +97 -0
- ai_review/tests/suites/services/review/test_service.py +64 -97
- ai_review/tests/suites/services/vcs/bitbucket/test_adapter.py +109 -0
- ai_review/tests/suites/services/vcs/bitbucket/{test_service.py → test_client.py} +88 -1
- ai_review/tests/suites/services/vcs/github/test_adapter.py +162 -0
- ai_review/tests/suites/services/vcs/github/{test_service.py → test_client.py} +102 -2
- ai_review/tests/suites/services/vcs/gitlab/test_adapter.py +105 -0
- ai_review/tests/suites/services/vcs/gitlab/{test_service.py → test_client.py} +99 -1
- {xai_review-0.26.0.dist-info → xai_review-0.28.0.dist-info}/METADATA +8 -5
- {xai_review-0.26.0.dist-info → xai_review-0.28.0.dist-info}/RECORD +160 -75
- ai_review/services/review/inline/service.py +0 -54
- ai_review/services/review/inline/types.py +0 -11
- ai_review/tests/fixtures/services/review/summary.py +0 -19
- ai_review/tests/suites/services/review/inline/test_service.py +0 -107
- /ai_review/{services/review/inline → libs/llm}/__init__.py +0 -0
- /ai_review/services/review/{policy → internal}/__init__.py +0 -0
- /ai_review/services/review/{summary → internal/inline}/__init__.py +0 -0
- /ai_review/services/review/{inline → internal/inline}/schema.py +0 -0
- /ai_review/{tests/suites/services/review/inline → services/review/internal/inline_reply}/__init__.py +0 -0
- /ai_review/{tests/suites/services/review → services/review/internal}/policy/__init__.py +0 -0
- /ai_review/{tests/suites/services/review → services/review/internal}/summary/__init__.py +0 -0
- /ai_review/services/review/{summary → internal/summary}/schema.py +0 -0
- {xai_review-0.26.0.dist-info → xai_review-0.28.0.dist-info}/WHEEL +0 -0
- {xai_review-0.26.0.dist-info → xai_review-0.28.0.dist-info}/entry_points.txt +0 -0
- {xai_review-0.26.0.dist-info → xai_review-0.28.0.dist-info}/licenses/LICENSE +0 -0
- {xai_review-0.26.0.dist-info → xai_review-0.28.0.dist-info}/top_level.txt +0 -0
ai_review/cli/main.py
CHANGED
|
@@ -3,8 +3,10 @@ import asyncio
|
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
5
|
from ai_review.cli.commands.run_context_review import run_context_review_command
|
|
6
|
+
from ai_review.cli.commands.run_inline_reply_review import run_inline_reply_review_command
|
|
6
7
|
from ai_review.cli.commands.run_inline_review import run_inline_review_command
|
|
7
8
|
from ai_review.cli.commands.run_review import run_review_command
|
|
9
|
+
from ai_review.cli.commands.run_summary_reply_review import run_summary_reply_review_command
|
|
8
10
|
from ai_review.cli.commands.run_summary_review import run_summary_review_command
|
|
9
11
|
from ai_review.config import settings
|
|
10
12
|
|
|
@@ -43,6 +45,21 @@ def run_summary():
|
|
|
43
45
|
typer.secho("AI review completed successfully!", fg=typer.colors.GREEN, bold=True)
|
|
44
46
|
|
|
45
47
|
|
|
48
|
+
@app.command("run-inline-reply")
|
|
49
|
+
def run_inline_reply():
|
|
50
|
+
"""Run only the inline reply review"""
|
|
51
|
+
typer.secho("Starting inline reply AI review...", fg=typer.colors.CYAN)
|
|
52
|
+
asyncio.run(run_inline_reply_review_command())
|
|
53
|
+
typer.secho("AI review completed successfully!", fg=typer.colors.GREEN, bold=True)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@app.command("run-summary-reply")
|
|
57
|
+
def run_summary_reply():
|
|
58
|
+
typer.secho("Starting summary reply AI review...", fg=typer.colors.CYAN)
|
|
59
|
+
asyncio.run(run_summary_reply_review_command())
|
|
60
|
+
typer.secho("AI review completed successfully!", fg=typer.colors.GREEN, bold=True)
|
|
61
|
+
|
|
62
|
+
|
|
46
63
|
@app.command("show-config")
|
|
47
64
|
def show_config():
|
|
48
65
|
"""Show the current resolved configuration"""
|
|
@@ -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,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from pydantic import BaseModel, Field, ConfigDict
|
|
2
2
|
|
|
3
|
+
from ai_review.clients.bitbucket.pr.schema.user import BitbucketUserSchema
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
class BitbucketCommentContentSchema(BaseModel):
|
|
5
7
|
raw: str
|
|
@@ -15,30 +17,47 @@ class BitbucketCommentInlineSchema(BaseModel):
|
|
|
15
17
|
from_line: int | None = Field(alias="from", default=None)
|
|
16
18
|
|
|
17
19
|
|
|
20
|
+
class BitbucketCommentParentSchema(BaseModel):
|
|
21
|
+
id: int
|
|
22
|
+
|
|
23
|
+
|
|
18
24
|
class BitbucketPRCommentSchema(BaseModel):
|
|
19
25
|
id: int
|
|
26
|
+
user: BitbucketUserSchema | None = None
|
|
27
|
+
parent: BitbucketCommentParentSchema | None = None
|
|
20
28
|
inline: BitbucketCommentInlineSchema | None = None
|
|
21
29
|
content: BitbucketCommentContentSchema
|
|
22
30
|
|
|
23
31
|
|
|
24
32
|
class BitbucketGetPRCommentsQuerySchema(BaseModel):
|
|
25
|
-
|
|
33
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
34
|
+
|
|
35
|
+
page: int = 1
|
|
36
|
+
page_len: int = Field(alias="pagelen", default=100)
|
|
26
37
|
|
|
27
38
|
|
|
28
39
|
class BitbucketGetPRCommentsResponseSchema(BaseModel):
|
|
40
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
41
|
+
|
|
29
42
|
size: int
|
|
30
43
|
page: int | None = None
|
|
31
44
|
next: str | None = None
|
|
32
45
|
values: list[BitbucketPRCommentSchema]
|
|
33
|
-
|
|
46
|
+
page_len: int = Field(alias="pagelen")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class BitbucketParentSchema(BaseModel):
|
|
50
|
+
id: int
|
|
34
51
|
|
|
35
52
|
|
|
36
53
|
class BitbucketCreatePRCommentRequestSchema(BaseModel):
|
|
54
|
+
parent: BitbucketParentSchema | None = None
|
|
37
55
|
inline: BitbucketCommentInlineSchema | None = None
|
|
38
56
|
content: BitbucketCommentContentSchema
|
|
39
57
|
|
|
40
58
|
|
|
41
59
|
class BitbucketCreatePRCommentResponseSchema(BaseModel):
|
|
42
60
|
id: int
|
|
61
|
+
parent: BitbucketParentSchema | None = None
|
|
43
62
|
inline: BitbucketCommentInlineSchema | None = None
|
|
44
63
|
content: BitbucketCommentContentSchema
|
|
@@ -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,25 +1,34 @@
|
|
|
1
1
|
from httpx import Response, QueryParams
|
|
2
2
|
|
|
3
3
|
from ai_review.clients.github.pr.schema.comments import (
|
|
4
|
+
GitHubPRCommentSchema,
|
|
5
|
+
GitHubIssueCommentSchema,
|
|
4
6
|
GitHubGetPRCommentsQuerySchema,
|
|
5
7
|
GitHubGetPRCommentsResponseSchema,
|
|
8
|
+
GitHubGetIssueCommentsResponseSchema,
|
|
6
9
|
GitHubCreateIssueCommentRequestSchema,
|
|
7
10
|
GitHubCreateIssueCommentResponseSchema,
|
|
11
|
+
GitHubCreateReviewReplyRequestSchema,
|
|
8
12
|
GitHubCreateReviewCommentRequestSchema,
|
|
9
13
|
GitHubCreateReviewCommentResponseSchema
|
|
10
14
|
)
|
|
11
15
|
from ai_review.clients.github.pr.schema.files import (
|
|
16
|
+
GitHubPRFileSchema,
|
|
12
17
|
GitHubGetPRFilesQuerySchema,
|
|
13
18
|
GitHubGetPRFilesResponseSchema
|
|
14
19
|
)
|
|
15
20
|
from ai_review.clients.github.pr.schema.pull_request import GitHubGetPRResponseSchema
|
|
16
21
|
from ai_review.clients.github.pr.schema.reviews import (
|
|
22
|
+
GitHubPRReviewSchema,
|
|
17
23
|
GitHubGetPRReviewsQuerySchema,
|
|
18
24
|
GitHubGetPRReviewsResponseSchema
|
|
19
25
|
)
|
|
20
26
|
from ai_review.clients.github.pr.types import GitHubPullRequestsHTTPClientProtocol
|
|
27
|
+
from ai_review.clients.github.tools import github_has_next_page
|
|
28
|
+
from ai_review.config import settings
|
|
21
29
|
from ai_review.libs.http.client import HTTPClient
|
|
22
30
|
from ai_review.libs.http.handlers import HTTPClientError, handle_http_error
|
|
31
|
+
from ai_review.libs.http.paginate import paginate
|
|
23
32
|
|
|
24
33
|
|
|
25
34
|
class GitHubPullRequestsHTTPClientError(HTTPClientError):
|
|
@@ -70,6 +79,19 @@ class GitHubPullRequestsHTTPClient(HTTPClient, GitHubPullRequestsHTTPClientProto
|
|
|
70
79
|
query=QueryParams(**query.model_dump())
|
|
71
80
|
)
|
|
72
81
|
|
|
82
|
+
@handle_http_error(client="GitHubPullRequestsHTTPClient", exception=GitHubPullRequestsHTTPClientError)
|
|
83
|
+
async def create_review_reply_api(
|
|
84
|
+
self,
|
|
85
|
+
owner: str,
|
|
86
|
+
repo: str,
|
|
87
|
+
pull_number: str,
|
|
88
|
+
request: GitHubCreateReviewReplyRequestSchema,
|
|
89
|
+
) -> Response:
|
|
90
|
+
return await self.post(
|
|
91
|
+
f"/repos/{owner}/{repo}/pulls/{pull_number}/comments",
|
|
92
|
+
json=request.model_dump(),
|
|
93
|
+
)
|
|
94
|
+
|
|
73
95
|
@handle_http_error(client="GitHubPullRequestsHTTPClient", exception=GitHubPullRequestsHTTPClientError)
|
|
74
96
|
async def create_review_comment_api(
|
|
75
97
|
self,
|
|
@@ -114,24 +136,87 @@ class GitHubPullRequestsHTTPClient(HTTPClient, GitHubPullRequestsHTTPClientProto
|
|
|
114
136
|
return GitHubGetPRResponseSchema.model_validate_json(response.text)
|
|
115
137
|
|
|
116
138
|
async def get_files(self, owner: str, repo: str, pull_number: str) -> GitHubGetPRFilesResponseSchema:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
139
|
+
async def fetch_page(page: int) -> Response:
|
|
140
|
+
query = GitHubGetPRFilesQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
|
|
141
|
+
return await self.get_files_api(owner, repo, pull_number, query)
|
|
142
|
+
|
|
143
|
+
def extract_items(response: Response) -> list[GitHubPRFileSchema]:
|
|
144
|
+
result = GitHubGetPRFilesResponseSchema.model_validate_json(response.text)
|
|
145
|
+
return result.root
|
|
146
|
+
|
|
147
|
+
items = await paginate(
|
|
148
|
+
max_pages=settings.vcs.pagination.max_pages,
|
|
149
|
+
fetch_page=fetch_page,
|
|
150
|
+
extract_items=extract_items,
|
|
151
|
+
has_next_page=github_has_next_page
|
|
152
|
+
)
|
|
153
|
+
return GitHubGetPRFilesResponseSchema(root=items)
|
|
120
154
|
|
|
121
|
-
async def get_issue_comments(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
155
|
+
async def get_issue_comments(
|
|
156
|
+
self,
|
|
157
|
+
owner: str,
|
|
158
|
+
repo: str,
|
|
159
|
+
issue_number: str
|
|
160
|
+
) -> GitHubGetIssueCommentsResponseSchema:
|
|
161
|
+
async def fetch_page(page: int) -> Response:
|
|
162
|
+
query = GitHubGetPRCommentsQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
|
|
163
|
+
return await self.get_issue_comments_api(owner, repo, issue_number, query)
|
|
164
|
+
|
|
165
|
+
def extract_items(response: Response) -> list[GitHubIssueCommentSchema]:
|
|
166
|
+
result = GitHubGetIssueCommentsResponseSchema.model_validate_json(response.text)
|
|
167
|
+
return result.root
|
|
168
|
+
|
|
169
|
+
items = await paginate(
|
|
170
|
+
max_pages=settings.vcs.pagination.max_pages,
|
|
171
|
+
fetch_page=fetch_page,
|
|
172
|
+
extract_items=extract_items,
|
|
173
|
+
has_next_page=github_has_next_page
|
|
174
|
+
)
|
|
175
|
+
return GitHubGetIssueCommentsResponseSchema(root=items)
|
|
125
176
|
|
|
126
177
|
async def get_review_comments(self, owner: str, repo: str, pull_number: str) -> GitHubGetPRCommentsResponseSchema:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
178
|
+
async def fetch_page(page: int) -> Response:
|
|
179
|
+
query = GitHubGetPRCommentsQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
|
|
180
|
+
return await self.get_review_comments_api(owner, repo, pull_number, query)
|
|
181
|
+
|
|
182
|
+
def extract_items(response: Response) -> list[GitHubPRCommentSchema]:
|
|
183
|
+
result = GitHubGetPRCommentsResponseSchema.model_validate_json(response.text)
|
|
184
|
+
return result.root
|
|
185
|
+
|
|
186
|
+
items = await paginate(
|
|
187
|
+
max_pages=settings.vcs.pagination.max_pages,
|
|
188
|
+
fetch_page=fetch_page,
|
|
189
|
+
extract_items=extract_items,
|
|
190
|
+
has_next_page=github_has_next_page
|
|
191
|
+
)
|
|
192
|
+
return GitHubGetPRCommentsResponseSchema(root=items)
|
|
130
193
|
|
|
131
194
|
async def get_reviews(self, owner: str, repo: str, pull_number: str) -> GitHubGetPRReviewsResponseSchema:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
195
|
+
async def fetch_page(page: int) -> Response:
|
|
196
|
+
query = GitHubGetPRReviewsQuerySchema(page=page, per_page=settings.vcs.pagination.per_page)
|
|
197
|
+
return await self.get_reviews_api(owner, repo, pull_number, query)
|
|
198
|
+
|
|
199
|
+
def extract_items(response: Response) -> list[GitHubPRReviewSchema]:
|
|
200
|
+
result = GitHubGetPRReviewsResponseSchema.model_validate_json(response.text)
|
|
201
|
+
return result.root
|
|
202
|
+
|
|
203
|
+
items = await paginate(
|
|
204
|
+
max_pages=settings.vcs.pagination.max_pages,
|
|
205
|
+
fetch_page=fetch_page,
|
|
206
|
+
extract_items=extract_items,
|
|
207
|
+
has_next_page=github_has_next_page
|
|
208
|
+
)
|
|
209
|
+
return GitHubGetPRReviewsResponseSchema(root=items)
|
|
210
|
+
|
|
211
|
+
async def create_review_reply(
|
|
212
|
+
self,
|
|
213
|
+
owner: str,
|
|
214
|
+
repo: str,
|
|
215
|
+
pull_number: str,
|
|
216
|
+
request: GitHubCreateReviewReplyRequestSchema,
|
|
217
|
+
) -> GitHubCreateReviewCommentResponseSchema:
|
|
218
|
+
response = await self.create_review_reply_api(owner, repo, pull_number, request)
|
|
219
|
+
return GitHubCreateReviewCommentResponseSchema.model_validate_json(response.text)
|
|
135
220
|
|
|
136
221
|
async def create_review_comment(
|
|
137
222
|
self,
|
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
from pydantic import BaseModel, RootModel
|
|
2
2
|
|
|
3
|
+
from ai_review.clients.github.pr.schema.user import GitHubUserSchema
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GitHubIssueCommentSchema(BaseModel):
|
|
7
|
+
"""Represents a top-level comment in a PR discussion (issue-level)."""
|
|
8
|
+
id: int
|
|
9
|
+
body: str
|
|
10
|
+
user: GitHubUserSchema | None = None
|
|
11
|
+
|
|
3
12
|
|
|
4
13
|
class GitHubPRCommentSchema(BaseModel):
|
|
14
|
+
"""Represents an inline code review comment on a specific line in a PR."""
|
|
5
15
|
id: int
|
|
6
16
|
body: str
|
|
7
17
|
path: str | None = None
|
|
8
18
|
line: int | None = None
|
|
19
|
+
user: GitHubUserSchema | None = None
|
|
20
|
+
in_reply_to_id: int | None = None
|
|
9
21
|
|
|
10
22
|
|
|
11
23
|
class GitHubGetPRCommentsQuerySchema(BaseModel):
|
|
12
|
-
|
|
24
|
+
page: int = 1
|
|
25
|
+
per_page: int = 100
|
|
13
26
|
|
|
14
27
|
|
|
15
28
|
class GitHubGetPRCommentsResponseSchema(RootModel[list[GitHubPRCommentSchema]]):
|
|
16
29
|
root: list[GitHubPRCommentSchema]
|
|
17
30
|
|
|
18
31
|
|
|
32
|
+
class GitHubGetIssueCommentsResponseSchema(RootModel[list[GitHubIssueCommentSchema]]):
|
|
33
|
+
root: list[GitHubIssueCommentSchema]
|
|
34
|
+
|
|
35
|
+
|
|
19
36
|
class GitHubCreateIssueCommentRequestSchema(BaseModel):
|
|
20
37
|
body: str
|
|
21
38
|
|
|
@@ -25,6 +42,11 @@ class GitHubCreateIssueCommentResponseSchema(BaseModel):
|
|
|
25
42
|
body: str
|
|
26
43
|
|
|
27
44
|
|
|
45
|
+
class GitHubCreateReviewReplyRequestSchema(BaseModel):
|
|
46
|
+
body: str
|
|
47
|
+
in_reply_to: int
|
|
48
|
+
|
|
49
|
+
|
|
28
50
|
class GitHubCreateReviewCommentRequestSchema(BaseModel):
|
|
29
51
|
body: str
|
|
30
52
|
path: str
|
|
@@ -2,7 +2,9 @@ from typing import Protocol
|
|
|
2
2
|
|
|
3
3
|
from ai_review.clients.github.pr.schema.comments import (
|
|
4
4
|
GitHubGetPRCommentsResponseSchema,
|
|
5
|
+
GitHubGetIssueCommentsResponseSchema,
|
|
5
6
|
GitHubCreateIssueCommentResponseSchema,
|
|
7
|
+
GitHubCreateReviewReplyRequestSchema,
|
|
6
8
|
GitHubCreateReviewCommentResponseSchema,
|
|
7
9
|
GitHubCreateReviewCommentRequestSchema,
|
|
8
10
|
)
|
|
@@ -21,7 +23,7 @@ class GitHubPullRequestsHTTPClientProtocol(Protocol):
|
|
|
21
23
|
owner: str,
|
|
22
24
|
repo: str,
|
|
23
25
|
issue_number: str
|
|
24
|
-
) ->
|
|
26
|
+
) -> GitHubGetIssueCommentsResponseSchema: ...
|
|
25
27
|
|
|
26
28
|
async def get_review_comments(
|
|
27
29
|
self,
|
|
@@ -32,6 +34,14 @@ class GitHubPullRequestsHTTPClientProtocol(Protocol):
|
|
|
32
34
|
|
|
33
35
|
async def get_reviews(self, owner: str, repo: str, pull_number: str) -> GitHubGetPRReviewsResponseSchema: ...
|
|
34
36
|
|
|
37
|
+
async def create_review_reply(
|
|
38
|
+
self,
|
|
39
|
+
owner: str,
|
|
40
|
+
repo: str,
|
|
41
|
+
comment_id: str,
|
|
42
|
+
request: GitHubCreateReviewReplyRequestSchema,
|
|
43
|
+
) -> GitHubCreateReviewCommentResponseSchema: ...
|
|
44
|
+
|
|
35
45
|
async def create_review_comment(
|
|
36
46
|
self,
|
|
37
47
|
owner: str,
|