package httpapi import ( "net/http" "time" "github.com/easyai/easyai-ai-gateway/apps/api/internal/executionpool" ) type AdminExecutionPoolWorker struct { WorkerID string `json:"workerId"` InstanceID string `json:"instanceId"` Revision string `json:"revision,omitempty"` ProtocolVersion string `json:"protocolVersion"` Capabilities map[string]any `json:"capabilities"` Allocated int `json:"allocated"` SafeCapacity int `json:"safeCapacity"` ActiveTasks int `json:"activeTasks"` PressureState string `json:"pressureState"` HeartbeatAt time.Time `json:"heartbeatAt"` } type AdminExecutionPool struct { PoolID string `json:"poolId"` Labels map[string]string `json:"labels"` Capabilities map[string]any `json:"capabilities"` State string `json:"state"` Capacity *AdminExecutionPoolCapacity `json:"capacity,omitempty"` Workers []AdminExecutionPoolWorker `json:"workers"` } type AdminExecutionPoolCapacity struct { WorkerCount int `json:"workerCount"` Allocated int `json:"allocated"` SafeCapacity int `json:"safeCapacity"` ActiveTasks int `json:"activeTasks"` HeavyCapacity int `json:"heavyCapacity"` HeavyTasks int `json:"heavyTasks"` QueueDepth int `json:"queueDepth"` EstimatedQueueWaitMilli int64 `json:"estimatedQueueWaitMs"` ResourceHeadroom float64 `json:"resourceHeadroom"` Stability float64 `json:"stability"` Critical bool `json:"critical"` SampledAt time.Time `json:"sampledAt"` } type AdminRouteHealth struct { PoolID string `json:"poolId"` RouteProfileKey string `json:"routeProfileKey"` State string `json:"state"` SuccessRate float64 `json:"successRate"` ConnectTLSP95Milli int64 `json:"connectTlsP95Ms"` FirstByteP95Milli int64 `json:"firstByteP95Ms"` UploadBytesPerSecond float64 `json:"uploadBytesPerSecond"` JitterP95Milli int64 `json:"jitterP95Ms"` ConsecutiveFailures int `json:"consecutiveFailures"` ConsecutiveSuccesses int `json:"consecutiveSuccesses"` SampleCount int `json:"sampleCount"` SampledAt time.Time `json:"sampledAt"` ExpiresAt time.Time `json:"expiresAt"` } type AdminExecutionPoolResponse struct { Pools []AdminExecutionPool `json:"pools"` RouteHealth []AdminRouteHealth `json:"routeHealth"` CapturedAt time.Time `json:"capturedAt"` } // listExecutionPools godoc // @Summary 查询逻辑执行池、Worker 容量和上游路由健康状态 // @Tags Admin Runtime // @Produce json // @Success 200 {object} AdminExecutionPoolResponse // @Failure 500 {object} ErrorEnvelope // @Security BearerAuth // @Router /api/admin/runtime/execution-pools [get] func (s *Server) listExecutionPools(w http.ResponseWriter, r *http.Request) { now := time.Now() pools, err := s.coordinationStore.ListExecutionPools(r.Context()) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } workers, err := s.coordinationStore.ListWorkers(r.Context(), now) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } capacities, err := s.coordinationStore.ListCapacity(r.Context(), now) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } health, err := s.coordinationStore.ListAllRouteHealth(r.Context()) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } capacityByPool := make(map[string]executionpool.CapacitySnapshot, len(capacities)) for _, capacity := range capacities { capacityByPool[capacity.PoolID] = capacity } workersByPool := make(map[string][]AdminExecutionPoolWorker) for _, worker := range workers { workersByPool[worker.PoolID] = append(workersByPool[worker.PoolID], AdminExecutionPoolWorker{ WorkerID: worker.WorkerID, InstanceID: worker.InstanceID, Revision: worker.Revision, ProtocolVersion: worker.ProtocolVersion, Capabilities: worker.Capabilities, Allocated: worker.Allocated, SafeCapacity: worker.SafeCapacity, ActiveTasks: worker.ActiveTasks, PressureState: worker.PressureState, HeartbeatAt: worker.HeartbeatAt, }) } response := AdminExecutionPoolResponse{ Pools: make([]AdminExecutionPool, 0, len(pools)), RouteHealth: make([]AdminRouteHealth, 0, len(health)), CapturedAt: now, } for _, item := range health { response.RouteHealth = append(response.RouteHealth, AdminRouteHealth{ PoolID: item.PoolID, RouteProfileKey: item.RouteProfileKey, State: string(item.State), SuccessRate: item.SuccessRate, ConnectTLSP95Milli: item.ConnectTLSP95.Milliseconds(), FirstByteP95Milli: item.FirstByteP95.Milliseconds(), UploadBytesPerSecond: item.UploadBytesPerSecond, JitterP95Milli: item.JitterP95.Milliseconds(), ConsecutiveFailures: item.ConsecutiveFailures, ConsecutiveSuccesses: item.ConsecutiveSuccesses, SampleCount: item.SampleCount, SampledAt: item.SampledAt, ExpiresAt: item.ExpiresAt, }) } for _, pool := range pools { item := AdminExecutionPool{ PoolID: pool.ID, Labels: pool.Labels, Capabilities: pool.Capabilities, State: string(pool.State), Workers: workersByPool[pool.ID], } if capacity, ok := capacityByPool[pool.ID]; ok { item.Capacity = &AdminExecutionPoolCapacity{ WorkerCount: capacity.WorkerCount, Allocated: capacity.Allocated, SafeCapacity: capacity.SafeCapacity, ActiveTasks: capacity.ActiveTasks, HeavyCapacity: capacity.HeavyCapacity, HeavyTasks: capacity.HeavyTasks, QueueDepth: capacity.QueueDepth, EstimatedQueueWaitMilli: capacity.EstimatedQueueWait.Milliseconds(), ResourceHeadroom: capacity.ResourceHeadroom, Stability: capacity.Stability, Critical: capacity.Critical, SampledAt: capacity.SampledAt, } } response.Pools = append(response.Pools, item) } writeJSON(w, http.StatusOK, response) }