diff --git a/internal/config/config.go b/internal/config/config.go index d19a52f..d6f47b5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -284,36 +284,6 @@ type Config struct { NetScan *NetScanConfig `json:"netscan,omitempty"` } -// PraxisConfig — maven's connection to the Praxis attention service. -type PraxisConfig struct { - // URL — the Praxis HTTP API base URL (e.g. "http://localhost:9742"). - URL string `json:"url,omitempty"` - - // Token — the shared bearer token sent on every request. Empty ⇒ calls - // go out unauthenticated, which is only appropriate on a loopback or - // unix-socket transport. Supports ${VAR} expansion, so the secret lives - // in deploy/telegram.env, not in the committed config. - Token string `json:"token,omitempty"` -} - -// NexusConfig — connection to the Nexus identity service. -type NexusConfig struct { - // URL — the Nexus HTTP API base URL (e.g. "http://localhost:9740"). - URL string `json:"url,omitempty"` - - // Token — shared bearer token; see PraxisConfig.Token. - Token string `json:"token,omitempty"` -} - -// HexisConfig — connection to the Hexis capability execution service. -type HexisConfig struct { - // URL — the Hexis HTTP API base URL (e.g. "http://localhost:9741"). - URL string `json:"url,omitempty"` - - // Token — shared bearer token; see PraxisConfig.Token. - Token string `json:"token,omitempty"` -} - // RoutineConfig — one scheduled routine. Cron is a standard 5-field expression // ("0 8 * * *" = 08:00 daily). Body is the RU text delivered verbatim (routines // are not LLM-phrased). Severity (1-4, default 1) drives routing: care-class @@ -869,44 +839,6 @@ const ( DefaultKiwixSnippetRunes = 1500 ) -// WorkstationConfig — the big model on the owner's desktop (workpc, a -// 7900 GRE with 16GB), fronted by mavgpud. -// -// homesrv cannot grow a GPU, so the resident Qwen3-1.7B is the floor and this -// is the preferred model above it (owner's call, 2026-08-02, docs/offload.md). -// The workstation is never assumed up: its card is often held by a CPT run and -// the machine sleeps. No block, or an empty URL, and homesrv behaves exactly as -// it does today. -// -// Only the prompt crosses the LAN, and the workstation is not "the box". The -// rules in CLAUDE.md about what may leave still apply. -type WorkstationConfig struct { - // URL — where mavgpud listens, e.g. "http://192.168.1.105:8080". Empty ⇒ - // the whole block is normalised to nil and nothing probes anything. - URL string `json:"url,omitempty"` - - // Health — the admission endpoint. Empty ⇒ URL + "/health", which is what - // mavgpud serves. It answers 503 while the card is held, and that is the - // signal, so it must be the supervisor's endpoint and not llama-server's. - Health string `json:"health,omitempty"` - - // Probe — how often admission is re-checked. 0 ⇒ DefaultWorkstationProbe. - // Nothing on the hot path waits for it: the answer is cached and read - // atomically, so this only sets how late Maven notices the card came back. - Probe Duration `json:"probe,omitempty"` - - // Timeout — the per-request budget for a completion on the workstation. - // 0 ⇒ DefaultWorkstationTimeout. A big model on a LAN host is slower than - // the resident one, and a request that overruns falls back to the floor. - Timeout Duration `json:"timeout,omitempty"` -} - -// Workstation defaults, applied in Normalise. -const ( - DefaultWorkstationProbe = 15 * time.Second - DefaultWorkstationTimeout = 90 * time.Second -) - // SearchConfig — the self-hosted SearXNG instance she searches with. // // External search is allowed and off unless configured (CLAUDE.md). Configuring @@ -1274,23 +1206,7 @@ func (c *Config) applyDefaults() { } } - // No address, no preferred model. An unconfigured workstation is the - // default deploy and must be indistinguishable from today. - if c.Workstation != nil && strings.TrimSpace(c.Workstation.URL) == "" { - c.Workstation = nil - } - if c.Workstation != nil { - w := c.Workstation - if strings.TrimSpace(w.Health) == "" { - w.Health = strings.TrimRight(w.URL, "/") + "/health" - } - if w.Probe <= 0 { - w.Probe = Duration(DefaultWorkstationProbe) - } - if w.Timeout <= 0 { - w.Timeout = Duration(DefaultWorkstationTimeout) - } - } + c.normaliseWorkstation() if c.Voice != nil { if c.Voice.RouterThreshold <= 0 { diff --git a/internal/config/ecosystem.go b/internal/config/ecosystem.go new file mode 100644 index 0000000..2a17c90 --- /dev/null +++ b/internal/config/ecosystem.go @@ -0,0 +1,41 @@ +package config + +// The ecosystem trio. Maven owns conversation and personal memory; it owns +// neither identity, nor operational state, nor execution. Each of these three +// is nil unless configured and each degrades on its own — an outage is a named +// gap in the answer, never a broken turn and never a guess. See docs/ecosystem.md. +// +// All three carry the same two fields, and they stay three types rather than one +// shared EndpointConfig on purpose: the block a reader greps for is the service +// they are debugging, and a shared type would put "url" in one place for three +// different trust levels. + +// PraxisConfig — maven's connection to the Praxis attention service. +type PraxisConfig struct { + // URL — the Praxis HTTP API base URL (e.g. "http://localhost:9742"). + URL string `json:"url,omitempty"` + + // Token — the shared bearer token sent on every request. Empty ⇒ calls + // go out unauthenticated, which is only appropriate on a loopback or + // unix-socket transport. Supports ${VAR} expansion, so the secret lives + // in deploy/telegram.env, not in the committed config. + Token string `json:"token,omitempty"` +} + +// NexusConfig — connection to the Nexus identity service. +type NexusConfig struct { + // URL — the Nexus HTTP API base URL (e.g. "http://localhost:9740"). + URL string `json:"url,omitempty"` + + // Token — shared bearer token; see PraxisConfig.Token. + Token string `json:"token,omitempty"` +} + +// HexisConfig — connection to the Hexis capability execution service. +type HexisConfig struct { + // URL — the Hexis HTTP API base URL (e.g. "http://localhost:9741"). + URL string `json:"url,omitempty"` + + // Token — shared bearer token; see PraxisConfig.Token. + Token string `json:"token,omitempty"` +} diff --git a/internal/config/workstation.go b/internal/config/workstation.go new file mode 100644 index 0000000..7a79813 --- /dev/null +++ b/internal/config/workstation.go @@ -0,0 +1,66 @@ +package config + +import ( + "strings" + "time" +) + +// WorkstationConfig — the big model on the owner's desktop (workpc, a +// 7900 GRE with 16GB), fronted by mavgpud. +// +// homesrv cannot grow a GPU, so the resident Qwen3-1.7B is the floor and this +// is the preferred model above it (owner's call, 2026-08-02, docs/offload.md). +// The workstation is never assumed up: its card is often held by a CPT run and +// the machine sleeps. No block, or an empty URL, and homesrv behaves exactly as +// it does today. +// +// Only the prompt crosses the LAN, and the workstation is not "the box". The +// rules in CLAUDE.md about what may leave still apply. +type WorkstationConfig struct { + // URL — where mavgpud listens, e.g. "http://192.168.1.105:8080". Empty ⇒ + // the whole block is normalised to nil and nothing probes anything. + URL string `json:"url,omitempty"` + + // Health — the admission endpoint. Empty ⇒ URL + "/health", which is what + // mavgpud serves. It answers 503 while the card is held, and that is the + // signal, so it must be the supervisor's endpoint and not llama-server's. + Health string `json:"health,omitempty"` + + // Probe — how often admission is re-checked. 0 ⇒ DefaultWorkstationProbe. + // Nothing on the hot path waits for it: the answer is cached and read + // atomically, so this only sets how late Maven notices the card came back. + Probe Duration `json:"probe,omitempty"` + + // Timeout — the per-request budget for a completion on the workstation. + // 0 ⇒ DefaultWorkstationTimeout. A big model on a LAN host is slower than + // the resident one, and a request that overruns falls back to the floor. + Timeout Duration `json:"timeout,omitempty"` +} + +// Workstation defaults, applied in normaliseWorkstation. +const ( + DefaultWorkstationProbe = 15 * time.Second + DefaultWorkstationTimeout = 90 * time.Second +) + +// normaliseWorkstation applies the block's defaults. No address, no preferred +// model: an unconfigured workstation is the default deploy and must be +// indistinguishable from today. +func (c *Config) normaliseWorkstation() { + if c.Workstation != nil && strings.TrimSpace(c.Workstation.URL) == "" { + c.Workstation = nil + } + if c.Workstation == nil { + return + } + w := c.Workstation + if strings.TrimSpace(w.Health) == "" { + w.Health = strings.TrimRight(w.URL, "/") + "/health" + } + if w.Probe <= 0 { + w.Probe = Duration(DefaultWorkstationProbe) + } + if w.Timeout <= 0 { + w.Timeout = Duration(DefaultWorkstationTimeout) + } +}