Files
AetherForge/server/internal/api/fleet_ai_handler.go
AetherForge 0002e5fd93
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Add Calibrate AI Control UI and fleet LLM backend wiring.
Operators toggle Logic gates vs AI Control on Settings, refresh local Ollama models, and save ai_endpoint settings via Calibrate PUT; server scheduler and agent snapshot/command paths support stateless 60s fleet decisions.
2026-06-07 02:14:28 -07:00

91 lines
2.4 KiB
Go

package api
import (
"encoding/json"
"net/http"
"strconv"
"strings"
fleetai "crypto-miner-server/internal/ai"
"crypto-miner-server/internal/db"
)
// FleetAIHandler serves Fleet AI Control API routes.
type FleetAIHandler struct {
config FleetAIConfigSource
db interface {
ListAIDecisions(agentID string, limit int) ([]db.AIDecisionRecord, error)
}
}
func NewFleetAIHandler(cfg FleetAIConfigSource, database interface {
ListAIDecisions(agentID string, limit int) ([]db.AIDecisionRecord, error)
}) *FleetAIHandler {
return &FleetAIHandler{config: cfg, db: database}
}
func (h *FleetAIHandler) GetConfig(w http.ResponseWriter, r *http.Request) {
if h.config == nil {
http.Error(w, "config unavailable", http.StatusServiceUnavailable)
return
}
writeJSON(w, h.config.GetFleetAIConfig())
}
func (h *FleetAIHandler) PutConfig(w http.ResponseWriter, r *http.Request) {
if h.config == nil {
http.Error(w, "config unavailable", http.StatusServiceUnavailable)
return
}
var body FleetAIConfigView
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, "invalid JSON", http.StatusBadRequest)
return
}
if body.AIDecisionIntervalSec < 0 {
http.Error(w, "ai_decision_interval_sec must be ≥ 0", http.StatusBadRequest)
return
}
if err := h.config.UpdateFleetAIConfig(body); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
writeJSON(w, h.config.GetFleetAIConfig())
}
func (h *FleetAIHandler) GetModels(w http.ResponseWriter, r *http.Request) {
endpoint := strings.TrimSpace(r.URL.Query().Get("endpoint"))
if endpoint == "" && h.config != nil {
endpoint = h.config.GetFleetAIConfig().AIEndpoint
}
models, err := fleetai.ListModels(r.Context(), endpoint)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
writeJSON(w, map[string]interface{}{"models": models, "endpoint": endpoint})
}
func (h *FleetAIHandler) GetDecisions(w http.ResponseWriter, r *http.Request) {
if h.db == nil {
writeJSON(w, []db.AIDecisionRecord{})
return
}
agentID := strings.TrimSpace(r.URL.Query().Get("agent_id"))
limit := 50
if raw := r.URL.Query().Get("limit"); raw != "" {
if n, err := strconv.Atoi(raw); err == nil && n > 0 {
limit = n
}
}
rows, err := h.db.ListAIDecisions(agentID, limit)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if rows == nil {
rows = []db.AIDecisionRecord{}
}
writeJSON(w, rows)
}