192 lines
6.7 KiB
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
|
|
}
|