Add Calibrate AI Control UI and fleet LLM backend wiring.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

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.
This commit is contained in:
AetherForge
2026-06-07 02:14:28 -07:00
parent 34afa28f81
commit 0002e5fd93
33 changed files with 2791 additions and 12 deletions

View File

@@ -0,0 +1,92 @@
package ai
import (
"context"
"sync"
"testing"
"time"
)
type mockSnap struct {
ids []string
snap AgentSnapshot
}
func (m *mockSnap) ConnectedAgentIDs() []string { return m.ids }
func (m *mockSnap) AgentSnapshot(string) (AgentSnapshot, bool) {
return m.snap, true
}
type mockExec struct {
mu sync.Mutex
calls []Command
}
func (m *mockExec) Execute(_ string, cmd Command) (string, error) {
m.mu.Lock()
m.calls = append(m.calls, cmd)
m.mu.Unlock()
return cmd.Type, nil
}
type mockCfg struct{ cfg Config }
func (m *mockCfg) AIConfig() Config { return m.cfg }
type mockStore struct {
mu sync.Mutex
rows []string
}
func (m *mockStore) InsertAIDecision(_, _, _, executed string) error {
m.mu.Lock()
m.rows = append(m.rows, executed)
m.mu.Unlock()
return nil
}
func TestSchedulerExecutesRestartCommand(t *testing.T) {
old := DecideFunc
defer func() { DecideFunc = old }()
DecideFunc = func(_ context.Context, _, _, _, _ string) (string, error) {
return `{"commands":[{"type":"restart_mining","args":{}}]}`, nil
}
exec := &mockExec{}
store := &mockStore{}
sched := NewScheduler(
&mockCfg{cfg: Config{Enabled: true, Endpoint: "http://test/v1", IntervalSec: 1}},
&mockSnap{ids: []string{"agent-1"}, snap: AgentSnapshot{AgentID: "agent-1", Name: "host"}},
exec,
store,
)
sched.lastRun["agent-1"] = time.Now().Add(-2 * time.Minute)
sched.Tick()
exec.mu.Lock()
n := len(exec.calls)
call := exec.calls
exec.mu.Unlock()
if n != 1 || call[0].Type != CmdRestartMining {
t.Fatalf("calls: %+v", call)
}
store.mu.Lock()
defer store.mu.Unlock()
if len(store.rows) != 1 || store.rows[0] != "restart_mining:restart_mining" {
t.Fatalf("store: %v", store.rows)
}
}
func TestSchedulerNoOpWhenDisabled(t *testing.T) {
exec := &mockExec{}
sched := NewScheduler(
&mockCfg{cfg: Config{Enabled: false}},
&mockSnap{ids: []string{"agent-1"}},
exec,
nil,
)
sched.Tick()
if len(exec.calls) != 0 {
t.Fatalf("expected no calls")
}
}