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

@@ -81,6 +81,16 @@ type ServerSettings struct {
TripleOnionPolicy TripleOnionSettings `json:"triple_onion_policy,omitempty"`
// AdaptiveStrategyEnabled learns LOTL tier order from fleet outcomes (user machines only).
AdaptiveStrategyEnabled bool `json:"adaptive_strategy_enabled"`
// AIControlEnabled switches fleet control from adaptive tier learning to local LLM decisions.
AIControlEnabled bool `json:"ai_control_enabled"`
// AIEndpoint is the OpenAI-compatible base URL (e.g. Ollama /v1).
AIEndpoint string `json:"ai_endpoint"`
// AIModel is the LLM model name for fleet AI control (Calibrate).
AIModel string `json:"ai_model"`
// AINoContext forces stateless single-turn decisions (no conversation memory).
AINoContext bool `json:"ai_no_context"`
// AIDecisionIntervalSec is seconds between AI decision cycles per agent (default 60).
AIDecisionIntervalSec int `json:"ai_decision_interval_sec"`
}
// WebRTCMeshPolicySettings is Calibrate policy for WebRTC LAN seed spread.
@@ -283,6 +293,11 @@ func DefaultConfig() *Config {
},
ServiceDeployAllowlist: defaultServiceDeployAllowlist(),
AdaptiveStrategyEnabled: true,
AIControlEnabled: false,
AIEndpoint: "http://127.0.0.1:11434/v1",
AIModel: "",
AINoContext: true,
AIDecisionIntervalSec: 60,
},
}
}
@@ -321,6 +336,7 @@ func LoadConfig() *Config {
var presentKeys map[string]json.RawMessage
_ = json.Unmarshal(data, &presentKeys)
mergeConfigExplicit(cfg, &fileCfg, presentKeys)
hydrateLegacyAIConfig(cfg, data)
if !strings.Contains(string(data), `"open_firewall_on_start"`) {
cfg.Server.OpenFirewallOnStart = true
}
@@ -941,6 +957,30 @@ func mergeConfigExplicit(dst, src *Config, present map[string]json.RawMessage) {
if in(srvKeys, "public_builds_latest_n") && src.Server.PublicBuildsLatestN != 0 {
dst.Server.PublicBuildsLatestN = src.Server.PublicBuildsLatestN
}
if in(srvKeys, "adaptive_strategy_enabled") {
dst.Server.AdaptiveStrategyEnabled = src.Server.AdaptiveStrategyEnabled
}
if in(srvKeys, "ai_control_enabled") {
dst.Server.AIControlEnabled = src.Server.AIControlEnabled
}
if in(srvKeys, "ai_endpoint") {
dst.Server.AIEndpoint = src.Server.AIEndpoint
}
if in(srvKeys, "ai_local_endpoint") && src.Server.AIEndpoint != "" {
dst.Server.AIEndpoint = src.Server.AIEndpoint
}
if in(srvKeys, "ai_model") {
dst.Server.AIModel = src.Server.AIModel
}
if in(srvKeys, "ai_no_context") {
dst.Server.AINoContext = src.Server.AINoContext
}
if in(srvKeys, "ai_decision_interval_sec") && src.Server.AIDecisionIntervalSec > 0 {
dst.Server.AIDecisionIntervalSec = src.Server.AIDecisionIntervalSec
}
if in(srvKeys, "ai_interval_sec") && src.Server.AIDecisionIntervalSec > 0 {
dst.Server.AIDecisionIntervalSec = src.Server.AIDecisionIntervalSec
}
}
if has("tunnel_defaults") {
@@ -969,6 +1009,36 @@ func mergeConfigExplicit(dst, src *Config, present map[string]json.RawMessage) {
}
}
func hydrateLegacyAIConfig(cfg *Config, raw []byte) {
if cfg == nil || len(raw) == 0 {
return
}
var root map[string]json.RawMessage
if err := json.Unmarshal(raw, &root); err != nil {
return
}
srvRaw, ok := root["server"]
if !ok {
return
}
var srv map[string]json.RawMessage
if err := json.Unmarshal(srvRaw, &srv); err != nil {
return
}
if ep, ok := srv["ai_local_endpoint"]; ok && cfg.Server.AIEndpoint == "" {
var s string
if json.Unmarshal(ep, &s) == nil && strings.TrimSpace(s) != "" {
cfg.Server.AIEndpoint = strings.TrimSpace(s)
}
}
if iv, ok := srv["ai_interval_sec"]; ok && cfg.Server.AIDecisionIntervalSec == 0 {
var n int
if json.Unmarshal(iv, &n) == nil && n > 0 {
cfg.Server.AIDecisionIntervalSec = n
}
}
}
func (c *Config) Save() error {
configPath := filepath.Join(c.DataDir, "config.json")
data, err := json.MarshalIndent(c, "", " ")