All checks were successful
Build and Release / Create Release (push) Has been skipped
Build and Release / Unit Tests (push) Successful in 6m49s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 7m6s
Build and Release / Lint (push) Successful in 7m15s
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Has been skipped
Build and Release / Build Binaries (amd64, darwin, macos) (push) Has been skipped
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Has been skipped
Build and Release / Build Binaries (arm64, darwin, macos) (push) Has been skipped
Build and Release / Build Binary (linux/arm64) (push) Has been skipped
Implement critical production readiness features for AI integration: per-request provider config, admin dashboard, workflow inspection, and plugin framework foundation. Per-Request Provider Config: - Add ProviderConfig struct to all AI request types - Update queue to resolve provider/model/API key from cascade (repo > org > system) - Pass resolved config to AI sidecar on every request - Fixes multi-tenant issue where all orgs shared sidecar's hardcoded config Admin AI Dashboard: - Add /admin/ai page with sidecar health status - Display global operation stats (total, 24h, success/fail/escalated counts) - Show operations by tier, top 5 repos, token usage - Recent operations table with repo, operation, status, duration - Add GetGlobalOperationStats model method Workflow Inspection: - Add InspectWorkflow client method and types - Implement workflow-inspect queue handler - Add notifier trigger on workflow file push - Analyzes YAML for syntax errors, security issues, best practices - Returns structured issues with line numbers and suggested fixes Plugin Framework (Phase 5 Foundation): - Add external plugin config loading from app.ini - Define ExternalPlugin interface and manager - Add plugin.proto contract (Initialize, Shutdown, HealthCheck, OnEvent, HandleHTTP) - Implement health monitoring with auto-restart for managed plugins - Add event routing to subscribed plugins - HTTP proxy support for plugin-served routes This completes Tasks 1-4 from the production readiness plan and establishes the foundation for managed plugin lifecycle.
121 lines
3.3 KiB
Go
121 lines
3.3 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"
|
|
ai_service "code.gitcaddy.com/server/v3/services/ai"
|
|
"code.gitcaddy.com/server/v3/services/context"
|
|
)
|
|
|
|
// 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(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, nil)
|
|
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, nil)
|
|
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": "AI service is not available",
|
|
})
|
|
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, nil)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusInternalServerError, map[string]string{
|
|
"error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, suggestions)
|
|
}
|