feature: implements button to start AI code review

This commit is contained in:
ievgenii.v 2025-10-27 15:16:13 +02:00
parent f6d1fc8f44
commit 32952f105b
6 changed files with 112 additions and 36 deletions

View file

@ -79,7 +79,8 @@ class HomeView(BaseAppView, DataGridAppView):
_users = UserModel().get_users(name_contains=query, only_active=active)
def maybe_skip_user(usr):
if skip_default_user and usr["username"] in [UserModel.cls.DEFAULT_USER, UserModel.cls.GHOST_USER]:
if (skip_default_user and usr["username"] == UserModel.cls.DEFAULT_USER
or usr["username"] in [UserModel.cls.GHOST_USER, UserModel.cls.AI_USER]):
return False
return True

View file

@ -825,6 +825,20 @@ def includeme(config):
renderer="json_ext",
)
config.add_route(
name="pullrequest_start_ai_code_review",
pattern=r"/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/ai/review",
repo_route=True,
repo_forbid_when_archived=True,
)
config.add_view(
RepoPullRequestsView,
attr="start_ai_code_review",
route_name="pullrequest_start_ai_code_review",
request_method="POST",
renderer="json_ext",
)
config.add_route(
name="pullrequest_merge",
pattern=r"/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge",

View file

@ -61,6 +61,7 @@ from rhodecode.model.forms import PullRequestForm
from rhodecode.model.meta import Session
from rhodecode.model.pull_request import PullRequestModel, MergeCheck
from rhodecode.model.scm import ScmModel
from rhodecode.model.settings import SettingsModel
log = logging.getLogger(__name__)
@ -360,6 +361,12 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
c.state_progressing = pull_request.is_state_changing()
c.pr_broadcast_channel = channelstream.pr_channel(pull_request)
rc_settings = SettingsModel()
ai_setting = rc_settings.get_setting_by_name("ai_features_enabled")
c.ai_enabled = False
if ai_setting:
c.ai_enabled = ai_setting.app_settings_value
_new_state = {
"created": PullRequest.STATE_CREATED,
}.get(self.request.GET.get("force_state"))
@ -1280,6 +1287,53 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
h.route_path("pullrequest_show", repo_name=target_repo, pull_request_id=pull_request.pull_request_id)
)
@LoginRequired()
@HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
def start_ai_code_review(self):
_ = self.request.translate
redirect_url = None
pull_request = PullRequest.get_or_404(self.request.matchdict["pull_request_id"])
if pull_request.is_closed():
log.debug("AI code review: forbidden because pull request is closed")
message = _("Cannot start review for closed pull requests.")
h.flash(message, category="error")
return {"response": True, "redirect_url": redirect_url}
log.debug("AI code review: started")
redirect_url = h.route_path(
"pullrequest_show",
repo_name=self.db_repo_name,
pull_request_id=pull_request.pull_request_id,
)
admin_permissions = (
"repository.admin",
"hg.admin",
)
is_admin = h.HasRepoPermissionAny(*admin_permissions)(
user=self._rhodecode_user, repo_name=pull_request.target_repo.repo_name
)
if is_admin:
log.debug("Initiating code review for pull request id: %s", pull_request.pull_request_id)
run_task(tasks.start_ai_code_review, pull_request.pull_request_id)
msg = _("AI code review has started. The review will be ready in about 1–3 minutes.")
h.flash(msg, category="success")
else:
log.debug("AI code review canceled, only admin can initiate AI code review.")
msg = _(
"AI code review is not available. This feature is in BETA and can only be initiated by an admin."
)
h.flash(
msg,
category="warning",
)
return {"response": True, "redirect_url": redirect_url}
@LoginRequired()
@NotAnonymous()
@HasRepoPermissionAnyDecorator("repository.read", "repository.write", "repository.admin")
@ -1312,40 +1366,7 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
do_update_branch_close = "close_branch_before_merging" in self.request.POST
if "review_members" in controls:
ai_user = User.get_ai_user()
ai_user_id = ai_user.user_id
members_ = controls["review_members"]
for i, member_data in enumerate(members_):
user_id = safe_int(member_data.get("user_id", -1))
if ai_user_id == user_id:
admin_permissions = (
"repository.admin",
"hg.admin",
)
is_admin = h.HasRepoPermissionAny(*admin_permissions)(
user=self._rhodecode_user, repo_name=pull_request.target_repo.repo_name
)
if is_admin:
log.debug("Initiating code review for pull request id: %s", pull_request.pull_request_id)
run_task(tasks.start_ai_code_review, pull_request.pull_request_id)
msg = _("AI code review has started. The review will be ready in about 1–3 minutes.")
h.flash(msg, category="success")
else:
log.debug("AI code review canceled, only admin can initiate AI code review.")
msg = _(
"AI code review is not available. This feature is in BETA and can only be initiated by an admin."
)
h.flash(
msg,
category="warning",
)
del members_[i]
break
self._update_reviewers(
c,
pull_request,

View file

@ -495,7 +495,11 @@ def start_ai_code_review(pull_request_id):
log = get_logger(start_ai_code_review)
log.info("Starting AI code review for pull request id: %s", pull_request_id)
rc_settings = SettingsModel()
ai_enabled = rc_settings.get_setting_by_name("ai_features_enabled")
ai_setting = rc_settings.get_setting_by_name("ai_features_enabled")
ai_enabled = False
if ai_setting:
ai_enabled = ai_setting.app_settings_value
if not ai_enabled:
log.info("AI code review is disabled.")
return

View file

@ -417,6 +417,15 @@ window.ReviewersController = function () {
};
this.startAiCodeReview = function (repo_name, pull_request_id) {
const url = `/${repo_name}/pull-request/${pull_request_id}/ai/review`
const postData = {};
const success = function (o) {
window.location.reload();
};
ajaxPOST(url, postData, success);
};
this.updateReviewers = function (repo_name, pull_request_id, role) {
if (role === 'reviewer') {
var postData = $('#reviewers input').serialize();

View file

@ -615,7 +615,6 @@
</div>
<div id="reviewers" class="right-sidebar-expanded-state pr-details-content reviewers">
<div id="review_rules" style="display: none" class="">
<strong>${_('Reviewer rules')}</strong>
@ -707,6 +706,24 @@
</div>
% endif
% if c.ai_enabled:
<div id="ai-section" class="sidebar-element clear-both">
<div class="right-sidebar-expanded-state pr-details-title">
<span class="sidebar-heading">
<i class="icon-info"></i>
${_('AI')}
</span>
</div>
<div class="btn-group btn-group-actions" style="margin-top: 10px">
<a id="ai-code-review" class="btn btn-primary no-margin">
${_('Start AI code review')}
</a>
</div>
##<input id="ai-code-review" class="btn" name="save" type="submit" value="${_('Start AI code review')}">
</div>
</div>
% endif
## TODOs
<div id="todosTable" class="sidebar-element clear-both">
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="TODOs">
@ -981,6 +998,16 @@ window.setObserversData = ${c.pull_request_set_observers_data_json | n};
return true;
});
const $aiCodeReviewBtn = $('#ai-code-review');
$aiCodeReviewBtn.on('click', function () {
$aiCodeReviewBtn.attr('disabled', 'disabled');
$aiCodeReviewBtn.addClass('disabled');
reviewersController.startAiCodeReview(
templateContext.repo_name,
templateContext.pull_request_data.pull_request_id,
);
});
$('#edit_pull_request').on('click', function (e) {
var title = $('#pr-title-input').val();
var description = codeMirrorInstance.getValue();