2
0
Files
logikonline f42c6c39f9
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
feat(ai-service): complete ai production readiness tasks
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.
2026-02-13 01:16:58 -05:00

225 lines
8.9 KiB
Handlebars

{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin ai")}}
<div class="admin ai">
<h4 class="ui top attached header">
{{ctx.Locale.Tr "admin.ai.title"}}
</h4>
<!-- Stat Tiles -->
<div class="ui attached segment">
<div style="display: flex; justify-content: space-around; text-align: center; flex-wrap: wrap; gap: 10px;">
<!-- Sidecar Status -->
<div style="padding: 15px 25px; background: var(--color-box-body); border: 1px solid var(--color-secondary); border-radius: 8px; min-width: 150px;">
{{if .SidecarHealthy}}
<div style="font-size: 2em; font-weight: bold; color: #21ba45;">{{svg "octicon-check-circle-fill" 32}}</div>
<div style="color: #21ba45; font-weight: bold; margin-top: 5px;">Online</div>
{{else}}
<div style="font-size: 2em; font-weight: bold; color: #db2828;">{{svg "octicon-x-circle-fill" 32}}</div>
<div style="color: #db2828; font-weight: bold; margin-top: 5px;">Offline</div>
{{end}}
<div style="color: var(--color-text-light); margin-top: 5px;">{{ctx.Locale.Tr "admin.ai.sidecar_status"}}</div>
{{if .SidecarVersion}}<div style="color: var(--color-text-light); font-size: 0.85em;">v{{.SidecarVersion}}</div>{{end}}
</div>
<!-- Total Ops -->
<div style="padding: 15px 25px; background: var(--color-box-body); border: 1px solid var(--color-secondary); border-radius: 8px; min-width: 150px;">
<div style="font-size: 2em; font-weight: bold; color: #2185d0;">{{.Stats.TotalOperations}}</div>
<div style="color: var(--color-text-light); margin-top: 5px;">{{ctx.Locale.Tr "admin.ai.total_operations"}}</div>
</div>
<!-- 24h Ops -->
<div style="padding: 15px 25px; background: var(--color-box-body); border: 1px solid var(--color-secondary); border-radius: 8px; min-width: 150px;">
<div style="font-size: 2em; font-weight: bold; color: #6435c9;">{{.Stats.Operations24h}}</div>
<div style="color: var(--color-text-light); margin-top: 5px;">{{ctx.Locale.Tr "admin.ai.operations_24h"}}</div>
</div>
<!-- Success Rate -->
<div style="padding: 15px 25px; background: var(--color-box-body); border: 1px solid var(--color-secondary); border-radius: 8px; min-width: 150px;">
<div style="font-size: 2em; font-weight: bold; color: #21ba45;">{{printf "%.1f" .SuccessRate}}%</div>
<div style="color: var(--color-text-light); margin-top: 5px;">{{ctx.Locale.Tr "admin.ai.success_rate"}}</div>
</div>
<!-- Tokens Used -->
<div style="padding: 15px 25px; background: var(--color-box-body); border: 1px solid var(--color-secondary); border-radius: 8px; min-width: 150px;">
<div style="font-size: 2em; font-weight: bold; color: #f2711c;">{{.TotalTokens}}</div>
<div style="color: var(--color-text-light); margin-top: 5px;">{{ctx.Locale.Tr "admin.ai.tokens_used"}}</div>
<div style="color: var(--color-text-light); font-size: 0.85em;">In: {{.Stats.TotalInputTokens}} / Out: {{.Stats.TotalOutputTokens}}</div>
</div>
</div>
</div>
<!-- Configuration Summary -->
<h4 class="ui attached header">
{{ctx.Locale.Tr "admin.ai.config"}}
</h4>
<div class="ui attached segment">
<div class="ui two column stackable grid">
<div class="column">
<table class="ui very basic table">
<tbody>
<tr>
<td><strong>AI Enabled</strong></td>
<td>
{{if .AIConfig.Enabled}}
<span class="ui small green label">On</span>
{{else}}
<span class="ui small red label">Off</span>
{{end}}
</td>
</tr>
<tr>
<td><strong>Provider</strong></td>
<td>{{.AIConfig.DefaultProvider}}</td>
</tr>
<tr>
<td><strong>Model</strong></td>
<td><code>{{.AIConfig.DefaultModel}}</code></td>
</tr>
<tr>
<td><strong>Service URL</strong></td>
<td><code>{{.AIConfig.ServiceURL}}</code></td>
</tr>
<tr>
<td><strong>Rate Limit</strong></td>
<td>{{.AIConfig.MaxOperationsPerHour}} ops/hr, {{.AIConfig.MaxTokensPerOperation}} tokens/op</td>
</tr>
</tbody>
</table>
</div>
<div class="column">
<table class="ui very basic table">
<tbody>
<tr>
<td><strong>Code Review</strong></td>
<td>{{if .AIConfig.EnableCodeReview}}<span class="ui small green label">On</span>{{else}}<span class="ui small red label">Off</span>{{end}}</td>
</tr>
<tr>
<td><strong>Issue Triage</strong></td>
<td>{{if .AIConfig.EnableIssueTriage}}<span class="ui small green label">On</span>{{else}}<span class="ui small red label">Off</span>{{end}}</td>
</tr>
<tr>
<td><strong>Doc Generation</strong></td>
<td>{{if .AIConfig.EnableDocGen}}<span class="ui small green label">On</span>{{else}}<span class="ui small red label">Off</span>{{end}}</td>
</tr>
<tr>
<td><strong>Explain Code</strong></td>
<td>{{if .AIConfig.EnableExplainCode}}<span class="ui small green label">On</span>{{else}}<span class="ui small red label">Off</span>{{end}}</td>
</tr>
<tr>
<td><strong>Chat</strong></td>
<td>{{if .AIConfig.EnableChat}}<span class="ui small green label">On</span>{{else}}<span class="ui small red label">Off</span>{{end}}</td>
</tr>
<tr>
<td><strong>Auto-Respond</strong></td>
<td>{{if .AIConfig.AllowAutoRespond}}<span class="ui small green label">On</span>{{else}}<span class="ui small red label">Off</span>{{end}}</td>
</tr>
<tr>
<td><strong>Auto-Review</strong></td>
<td>{{if .AIConfig.AllowAutoReview}}<span class="ui small green label">On</span>{{else}}<span class="ui small red label">Off</span>{{end}}</td>
</tr>
<tr>
<td><strong>Agent Mode</strong></td>
<td>{{if .AIConfig.AllowAgentMode}}<span class="ui small green label">On</span>{{else}}<span class="ui small red label">Off</span>{{end}}</td>
</tr>
</tbody>
</table>
</div>
</div>
{{if .ProviderStatus}}
<div class="ui divider"></div>
<h5>Provider Status</h5>
<div>
{{range $provider, $status := .ProviderStatus}}
<span class="ui label" style="margin: 2px; background-color: {{if eq $status "ok"}}#21ba45{{else if eq $status "healthy"}}#21ba45{{else}}#db2828{{end}}; color: white;">
{{$provider}}: {{$status}}
</span>
{{end}}
</div>
{{end}}
</div>
<!-- Status Breakdown -->
<h4 class="ui attached header">
{{ctx.Locale.Tr "admin.ai.stats"}}
</h4>
<div class="ui attached segment">
<div style="display: flex; gap: 15px; flex-wrap: wrap;">
<div class="ui small statistic" style="margin: 5px 15px;">
<div class="value" style="color: #21ba45;">{{.Stats.SuccessCount}}</div>
<div class="label">Success</div>
</div>
<div class="ui small statistic" style="margin: 5px 15px;">
<div class="value" style="color: #db2828;">{{.Stats.FailedCount}}</div>
<div class="label">Failed</div>
</div>
<div class="ui small statistic" style="margin: 5px 15px;">
<div class="value" style="color: #f2711c;">{{.Stats.EscalatedCount}}</div>
<div class="label">Escalated</div>
</div>
<div class="ui small statistic" style="margin: 5px 15px;">
<div class="value" style="color: #2185d0;">{{.Stats.PendingCount}}</div>
<div class="label">Pending</div>
</div>
{{range $tier, $count := .Stats.CountByTier}}
<div class="ui small statistic" style="margin: 5px 15px;">
<div class="value" style="color: #6435c9;">{{$count}}</div>
<div class="label">Tier {{$tier}}</div>
</div>
{{end}}
</div>
</div>
<!-- Recent Operations -->
<h4 class="ui attached header">
{{ctx.Locale.Tr "admin.ai.recent_operations"}}
</h4>
<div class="ui attached segment">
<table class="ui celled striped table">
<thead>
<tr>
<th>Time</th>
<th>Repo ID</th>
<th>Operation</th>
<th>Tier</th>
<th>Status</th>
<th>Duration</th>
<th>Provider</th>
</tr>
</thead>
<tbody>
{{range .RecentOps}}
<tr>
<td>{{DateUtils.TimeSince .CreatedUnix}}</td>
<td>{{.RepoID}}</td>
<td><code>{{.Operation}}</code></td>
<td>
<span class="ui small label" style="background-color: {{if eq .Tier 1}}#2185d0{{else}}#6435c9{{end}}; color: white;">
Tier {{.Tier}}
</span>
</td>
<td>
{{if eq .Status "success"}}
<span class="ui small green label">{{.Status}}</span>
{{else if eq .Status "failed"}}
<span class="ui small red label">{{.Status}}</span>
{{else if eq .Status "escalated"}}
<span class="ui small orange label">{{.Status}}</span>
{{else}}
<span class="ui small blue label">{{.Status}}</span>
{{end}}
</td>
<td>{{.DurationMs}}ms</td>
<td>
{{if .Provider}}{{.Provider}}{{else}}-{{end}}
{{if .Model}}<br><small style="color: var(--color-text-light);">{{.Model}}</small>{{end}}
</td>
</tr>
{{else}}
<tr>
<td colspan="7" class="center aligned">
<i>No recent operations</i>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
{{template "admin/layout_footer" .}}