Integrate GitCaddy AI service with support for code review, issue triage, documentation generation, code explanation, and chat interface. Add AI client module with HTTP communication, configuration settings, API routes (web and REST), service layer, and UI templates for issue sidebar. Include comprehensive configuration options in app.example.ini for enabling/disabling features and service connection settings.
144 lines
4.1 KiB
Go
144 lines
4.1 KiB
Go
// Copyright 2026 MarketAlly. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
issues_model "code.gitcaddy.com/server/v3/models/issues"
|
|
"code.gitcaddy.com/server/v3/modules/setting"
|
|
"code.gitcaddy.com/server/v3/modules/templates"
|
|
"code.gitcaddy.com/server/v3/services/context"
|
|
ai_service "code.gitcaddy.com/server/v3/services/ai"
|
|
)
|
|
|
|
// AIReviewPullRequest handles the AI review request for a pull request
|
|
func AIReviewPullRequest(ctx *context.Context) {
|
|
if !ai_service.IsEnabled() {
|
|
ctx.Flash.Error(ctx.Tr("repo.ai.service_unavailable"))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + ctx.PathParam("index"))
|
|
return
|
|
}
|
|
|
|
if !setting.AI.EnableCodeReview {
|
|
ctx.Flash.Error(ctx.Tr("repo.ai.code_review_disabled"))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + ctx.PathParam("index"))
|
|
return
|
|
}
|
|
|
|
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
|
if err != nil {
|
|
ctx.ServerError("GetIssueByIndex", err)
|
|
return
|
|
}
|
|
|
|
if !issue.IsPull {
|
|
ctx.NotFound("Not a pull request", nil)
|
|
return
|
|
}
|
|
|
|
pr, err := issues_model.GetPullRequestByIssueID(ctx, issue.ID)
|
|
if err != nil {
|
|
ctx.ServerError("GetPullRequestByIssueID", err)
|
|
return
|
|
}
|
|
|
|
review, err := ai_service.ReviewPullRequest(ctx, pr)
|
|
if err != nil {
|
|
ctx.Flash.Error(ctx.Tr("repo.ai.review_failed", err.Error()))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + ctx.PathParam("index"))
|
|
return
|
|
}
|
|
|
|
// Store the review result in session for display
|
|
ctx.Session.Set("ai_review_result", review)
|
|
|
|
ctx.Flash.Success(ctx.Tr("repo.ai.review_completed"))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + ctx.PathParam("index"))
|
|
}
|
|
|
|
// AITriageIssue handles the AI triage request for an issue
|
|
func AITriageIssue(ctx *context.Context) {
|
|
if !ai_service.IsEnabled() {
|
|
ctx.Flash.Error(ctx.Tr("repo.ai.service_unavailable"))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + ctx.PathParam("index"))
|
|
return
|
|
}
|
|
|
|
if !setting.AI.EnableIssueTriage {
|
|
ctx.Flash.Error(ctx.Tr("repo.ai.issue_triage_disabled"))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + ctx.PathParam("index"))
|
|
return
|
|
}
|
|
|
|
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
|
if err != nil {
|
|
ctx.ServerError("GetIssueByIndex", err)
|
|
return
|
|
}
|
|
|
|
triage, err := ai_service.TriageIssue(ctx, issue)
|
|
if err != nil {
|
|
ctx.Flash.Error(ctx.Tr("repo.ai.triage_failed", err.Error()))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + ctx.PathParam("index"))
|
|
return
|
|
}
|
|
|
|
// Store the triage result in session for display
|
|
ctx.Session.Set("ai_triage_result", triage)
|
|
|
|
ctx.Flash.Success(ctx.Tr("repo.ai.triage_completed"))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + ctx.PathParam("index"))
|
|
}
|
|
|
|
// AISuggestLabels handles the AI label suggestion request
|
|
func AISuggestLabels(ctx *context.Context) {
|
|
if !ai_service.IsEnabled() {
|
|
ctx.JSON(http.StatusServiceUnavailable, map[string]string{
|
|
"error": ctx.Tr("repo.ai.service_unavailable"),
|
|
})
|
|
return
|
|
}
|
|
|
|
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64("index"))
|
|
if err != nil {
|
|
ctx.JSON(http.StatusNotFound, map[string]string{
|
|
"error": "Issue not found",
|
|
})
|
|
return
|
|
}
|
|
|
|
suggestions, err := ai_service.SuggestLabels(ctx, issue)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusInternalServerError, map[string]string{
|
|
"error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, suggestions)
|
|
}
|
|
|
|
// IsAIEnabled is a template helper to check if AI is enabled
|
|
func IsAIEnabled() bool {
|
|
return ai_service.IsEnabled()
|
|
}
|
|
|
|
// IsAICodeReviewEnabled checks if AI code review is enabled
|
|
func IsAICodeReviewEnabled() bool {
|
|
return ai_service.IsEnabled() && setting.AI.EnableCodeReview
|
|
}
|
|
|
|
// IsAIIssueTriageEnabled checks if AI issue triage is enabled
|
|
func IsAIIssueTriageEnabled() bool {
|
|
return ai_service.IsEnabled() && setting.AI.EnableIssueTriage
|
|
}
|
|
|
|
func init() {
|
|
// Register template functions
|
|
templates.RegisterTemplateFunc("IsAIEnabled", IsAIEnabled)
|
|
templates.RegisterTemplateFunc("IsAICodeReviewEnabled", IsAICodeReviewEnabled)
|
|
templates.RegisterTemplateFunc("IsAIIssueTriageEnabled", IsAIIssueTriageEnabled)
|
|
}
|