iop/packages/go/config/normalize.go

192 lines
6.7 KiB
Go

package config
import (
"fmt"
"strings"
)
const AgentKindGenericNode = "generic-node"
// NormalizeAgentKind returns the canonical agent kind for a node definition.
// An empty value defaults to generic-node; any other unsupported value is
// rejected so misconfigured kinds fail at config load time.
func NormalizeAgentKind(kind string) (string, error) {
switch kind {
case "":
return AgentKindGenericNode, nil
case AgentKindGenericNode:
return kind, nil
default:
return "", fmt.Errorf("invalid agent_kind %q (allowed: %q)", kind, AgentKindGenericNode)
}
}
// NormalizeProviderType returns the canonical driver name for provider type.
func NormalizeProviderType(t string) string {
switch strings.ToLower(strings.TrimSpace(t)) {
case "openai_compat", "openai_api", "vllm", "vllm-mlx", "lemonade", "sglang", "seulgivibe_claude", "seulgivibe_openai":
return "openai_compat"
case "ollama":
return "ollama"
case "cli":
return "cli"
default:
return t
}
}
// NormalizeAdapters promotes legacy single-instance Ollama/Vllm fields into the
// typed instance slices and validates that all instance names are unique.
func NormalizeAdapters(a *AdaptersConf) error {
return normalizeAdapters(a)
}
func normalizeAdapters(a *AdaptersConf) error {
if a.Ollama.Enabled {
existing := findOllamaInstance(a.OllamaInstances, "ollama")
if existing == nil {
a.OllamaInstances = append([]OllamaInstanceConf{{
Name: "ollama",
Enabled: a.Ollama.Enabled,
BaseURL: a.Ollama.BaseURL,
ContextSize: a.Ollama.ContextSize,
Capacity: a.Ollama.Capacity,
MaxQueue: a.Ollama.MaxQueue,
QueueTimeoutMS: a.Ollama.QueueTimeoutMS,
RequestTimeoutMS: a.Ollama.RequestTimeoutMS,
}}, a.OllamaInstances...)
} else if !sameOllamaInstance(*existing, a.Ollama) {
return fmt.Errorf("ollama: legacy field conflicts with explicit instance %q: enabled/base_url/context_size/capacity/max_queue/queue_timeout_ms/request_timeout_ms mismatch", "ollama")
}
}
if a.Vllm.Enabled {
existing := findVllmInstance(a.VllmInstances, "vllm")
if existing == nil {
a.VllmInstances = append([]VllmInstanceConf{{
Name: "vllm",
Enabled: a.Vllm.Enabled,
Endpoint: a.Vllm.Endpoint,
Capacity: a.Vllm.Capacity,
MaxQueue: a.Vllm.MaxQueue,
QueueTimeoutMS: a.Vllm.QueueTimeoutMS,
RequestTimeoutMS: a.Vllm.RequestTimeoutMS,
}}, a.VllmInstances...)
} else if !sameVllmInstance(*existing, a.Vllm) {
return fmt.Errorf("vllm: legacy field conflicts with explicit instance %q: enabled/endpoint/capacity/max_queue/queue_timeout_ms/request_timeout_ms mismatch", "vllm")
}
}
if a.OpenAICompat.Enabled {
existing := findOpenAICompatInstance(a.OpenAICompatInstances, "openai_compat")
if existing == nil {
a.OpenAICompatInstances = append([]OpenAICompatInstanceConf{{
Name: "openai_compat",
Enabled: a.OpenAICompat.Enabled,
Provider: a.OpenAICompat.Provider,
Endpoint: a.OpenAICompat.Endpoint,
Headers: a.OpenAICompat.Headers,
Capacity: a.OpenAICompat.Capacity,
MaxQueue: a.OpenAICompat.MaxQueue,
QueueTimeoutMS: a.OpenAICompat.QueueTimeoutMS,
RequestTimeoutMS: a.OpenAICompat.RequestTimeoutMS,
}}, a.OpenAICompatInstances...)
} else if !sameOpenAICompatInstance(*existing, a.OpenAICompat) {
return fmt.Errorf("openai_compat: legacy field conflicts with explicit instance %q: enabled/provider/endpoint/headers/capacity/max_queue/queue_timeout_ms/request_timeout_ms mismatch", "openai_compat")
}
}
if err := checkUniqueNames("ollama_instances", func(i int) string { return a.OllamaInstances[i].Name }, len(a.OllamaInstances)); err != nil {
return err
}
if err := checkUniqueNames("vllm_instances", func(i int) string { return a.VllmInstances[i].Name }, len(a.VllmInstances)); err != nil {
return err
}
if err := checkUniqueNames("openai_compat_instances", func(i int) string { return a.OpenAICompatInstances[i].Name }, len(a.OpenAICompatInstances)); err != nil {
return err
}
for i, inst := range a.OllamaInstances {
field := fmt.Sprintf("ollama_instances[%d]", i)
if err := validateProviderQueueConfig(field, inst.Capacity, inst.MaxQueue, inst.QueueTimeoutMS, inst.RequestTimeoutMS); err != nil {
return err
}
}
for i, inst := range a.VllmInstances {
field := fmt.Sprintf("vllm_instances[%d]", i)
if err := validateProviderQueueConfig(field, inst.Capacity, inst.MaxQueue, inst.QueueTimeoutMS, inst.RequestTimeoutMS); err != nil {
return err
}
}
for i, inst := range a.OpenAICompatInstances {
field := fmt.Sprintf("openai_compat_instances[%d]", i)
if err := validateProviderQueueConfig(field, inst.Capacity, inst.MaxQueue, inst.QueueTimeoutMS, inst.RequestTimeoutMS); err != nil {
return err
}
}
return nil
}
func sameOllamaInstance(inst OllamaInstanceConf, legacy OllamaConf) bool {
return inst.Enabled == legacy.Enabled &&
inst.BaseURL == legacy.BaseURL &&
inst.ContextSize == legacy.ContextSize &&
inst.Capacity == legacy.Capacity &&
inst.MaxQueue == legacy.MaxQueue &&
inst.QueueTimeoutMS == legacy.QueueTimeoutMS &&
inst.RequestTimeoutMS == legacy.RequestTimeoutMS
}
func sameVllmInstance(inst VllmInstanceConf, legacy VllmConf) bool {
return inst.Enabled == legacy.Enabled &&
inst.Endpoint == legacy.Endpoint &&
inst.Capacity == legacy.Capacity &&
inst.MaxQueue == legacy.MaxQueue &&
inst.QueueTimeoutMS == legacy.QueueTimeoutMS &&
inst.RequestTimeoutMS == legacy.RequestTimeoutMS
}
func findOllamaInstance(instances []OllamaInstanceConf, name string) *OllamaInstanceConf {
for i := range instances {
if instances[i].Name == name {
return &instances[i]
}
}
return nil
}
func findVllmInstance(instances []VllmInstanceConf, name string) *VllmInstanceConf {
for i := range instances {
if instances[i].Name == name {
return &instances[i]
}
}
return nil
}
func sameOpenAICompatInstance(inst OpenAICompatInstanceConf, legacy OpenAICompatConf) bool {
if inst.Enabled != legacy.Enabled ||
inst.Provider != legacy.Provider ||
inst.Endpoint != legacy.Endpoint ||
inst.Capacity != legacy.Capacity ||
inst.MaxQueue != legacy.MaxQueue ||
inst.QueueTimeoutMS != legacy.QueueTimeoutMS ||
inst.RequestTimeoutMS != legacy.RequestTimeoutMS {
return false
}
if len(inst.Headers) != len(legacy.Headers) {
return false
}
for k, v := range inst.Headers {
if lv, ok := legacy.Headers[k]; !ok || lv != v {
return false
}
}
return true
}
func findOpenAICompatInstance(instances []OpenAICompatInstanceConf, name string) *OpenAICompatInstanceConf {
for i := range instances {
if instances[i].Name == name {
return &instances[i]
}
}
return nil
}