Add AI service dashboard endpoint
This commit is contained in:
85
internal/httpapi/dashboard.go
Normal file
85
internal/httpapi/dashboard.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"ai-service/internal/model"
|
||||
)
|
||||
|
||||
type dashboardResponse struct {
|
||||
At time.Time `json:"at"`
|
||||
Summary dashboardSummary `json:"summary"`
|
||||
Stats *model.Stats `json:"stats"`
|
||||
Providers providersStatusResponse `json:"providers"`
|
||||
Infra infraStatusResponse `json:"infra"`
|
||||
Jobs []*model.Job `json:"jobs"`
|
||||
}
|
||||
|
||||
type dashboardSummary struct {
|
||||
Pending int64 `json:"pending"`
|
||||
Running int64 `json:"running"`
|
||||
Done int64 `json:"done"`
|
||||
Failed int64 `json:"failed"`
|
||||
Cancelled int64 `json:"cancelled"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) {
|
||||
now := time.Now().UTC()
|
||||
ctx, cancel := contextWithTimeout(r, 12*time.Second)
|
||||
defer cancel()
|
||||
|
||||
stats, err := s.store.Stats(ctx)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
jobs, err := s.store.ListJobs(ctx, model.JobFilter{
|
||||
Statuses: []string{model.StatusFailed, model.StatusRunning},
|
||||
Limit: 40,
|
||||
})
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
resp := dashboardResponse{
|
||||
At: now,
|
||||
Summary: summarizeQueues(stats),
|
||||
Stats: stats,
|
||||
Providers: providersStatusResponse{
|
||||
At: now,
|
||||
Providers: []providerStatus{
|
||||
s.checkLLM(ctx),
|
||||
s.checkWhisperX(ctx),
|
||||
},
|
||||
},
|
||||
Infra: loadInfraSnapshot(r, s.cfg),
|
||||
Jobs: jobs,
|
||||
}
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func summarizeQueues(stats *model.Stats) dashboardSummary {
|
||||
var out dashboardSummary
|
||||
if stats == nil {
|
||||
return out
|
||||
}
|
||||
for _, row := range stats.Queues {
|
||||
switch row.Status {
|
||||
case model.StatusPending:
|
||||
out.Pending += row.Total
|
||||
case model.StatusRunning:
|
||||
out.Running += row.Total
|
||||
case model.StatusDone:
|
||||
out.Done += row.Total
|
||||
case model.StatusFailed:
|
||||
out.Failed += row.Total
|
||||
case model.StatusCancelled:
|
||||
out.Cancelled += row.Total
|
||||
}
|
||||
out.Total += row.Total
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ai-service/internal/config"
|
||||
)
|
||||
|
||||
type infraStatusResponse struct {
|
||||
@@ -17,15 +19,18 @@ type infraStatusResponse struct {
|
||||
}
|
||||
|
||||
func (s *Server) handleInfraStatus(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, loadInfraSnapshot(r, s.cfg))
|
||||
}
|
||||
|
||||
func loadInfraSnapshot(r *http.Request, cfg config.Config) infraStatusResponse {
|
||||
resp := infraStatusResponse{At: time.Now().UTC()}
|
||||
baseURL := strings.TrimRight(strings.TrimSpace(s.cfg.AIStatsSidecarURL), "/")
|
||||
baseURL := strings.TrimRight(strings.TrimSpace(cfg.AIStatsSidecarURL), "/")
|
||||
if baseURL == "" {
|
||||
resp.SidecarError = "AI stats sidecar is not configured"
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
return
|
||||
return resp
|
||||
}
|
||||
|
||||
timeout := s.cfg.AIStatsTimeout
|
||||
timeout := cfg.AIStatsTimeout
|
||||
if timeout <= 0 {
|
||||
timeout = 8 * time.Second
|
||||
}
|
||||
@@ -37,7 +42,7 @@ func (s *Server) handleInfraStatus(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
resp.Sidecar = sidecar
|
||||
}
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
return resp
|
||||
}
|
||||
|
||||
func fetchAIStatsSidecar(ctx context.Context, baseURL string, timeout time.Duration) (map[string]any, error) {
|
||||
|
||||
@@ -69,6 +69,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.handleProviderStatus(w, r)
|
||||
case r.Method == http.MethodGet && path == "/api/v1/infra/status":
|
||||
s.handleInfraStatus(w, r)
|
||||
case r.Method == http.MethodGet && path == "/api/v1/dashboard":
|
||||
s.handleDashboard(w, r)
|
||||
default:
|
||||
writeError(w, http.StatusNotFound, "not found")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user