iop/apps/node/internal/adapters/factory.go

142 lines
3.2 KiB
Go

package adapters
import (
"fmt"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/structpb"
"iop/apps/node/internal/adapters/cli"
"iop/apps/node/internal/adapters/mock"
"iop/apps/node/internal/adapters/ollama"
"iop/apps/node/internal/adapters/vllm"
"iop/packages/config"
iop "iop/proto/gen/iop"
)
// BuildFromPayload creates a Registry from a NodeConfigPayload received from edge.
func BuildFromPayload(payload *iop.NodeConfigPayload, logger *zap.Logger) (*Registry, error) {
reg := NewRegistry()
reg.Register(mock.New(logger))
for _, ac := range payload.GetAdapters() {
if !ac.GetEnabled() {
continue
}
switch ac.GetType() {
case "mock":
// mock is always registered above.
case "ollama":
cfg := ollamaConfFromStruct(ac.GetSettings())
reg.Register(ollama.New(cfg, logger))
case "vllm":
cfg := vllmConfFromStruct(ac.GetSettings())
reg.Register(vllm.New(cfg, logger))
case "cli":
cfg := cliConfFromStruct(ac.GetSettings())
reg.Register(cli.New(cfg, logger))
default:
return nil, fmt.Errorf("adapters: unknown adapter type %q", ac.GetType())
}
}
return reg, nil
}
func ollamaConfFromStruct(s *structpb.Struct) config.OllamaConf {
if s == nil {
return config.OllamaConf{}
}
m := s.AsMap()
cfg := config.OllamaConf{Enabled: true}
if v, ok := m["base_url"].(string); ok {
cfg.BaseURL = v
}
return cfg
}
func vllmConfFromStruct(s *structpb.Struct) config.VllmConf {
if s == nil {
return config.VllmConf{}
}
m := s.AsMap()
cfg := config.VllmConf{Enabled: true}
if v, ok := m["endpoint"].(string); ok {
cfg.Endpoint = v
}
return cfg
}
func cliConfFromStruct(s *structpb.Struct) config.CLIConf {
if s == nil {
return config.CLIConf{}
}
m := s.AsMap()
cfg := config.CLIConf{Enabled: true, Profiles: make(map[string]config.CLIProfileConf)}
if profiles, ok := m["profiles"].(map[string]any); ok {
for name, pAny := range profiles {
p, ok := pAny.(map[string]any)
if !ok {
continue
}
prof := config.CLIProfileConf{}
if cmd, ok := p["command"].(string); ok {
prof.Command = cmd
}
if args, ok := p["args"].([]any); ok {
for _, a := range args {
if s, ok := a.(string); ok {
prof.Args = append(prof.Args, s)
}
}
}
if envs, ok := p["env"].([]any); ok {
for _, e := range envs {
if s, ok := e.(string); ok {
prof.Env = append(prof.Env, s)
}
}
}
if v, ok := boolFromAny(p["persistent"]); ok {
prof.Persistent = v
}
if v, ok := boolFromAny(p["terminal"]); ok {
prof.Terminal = v
}
if v, ok := intFromAny(p["response_idle_timeout_ms"]); ok {
prof.ResponseIdleTimeoutMS = v
}
if v, ok := intFromAny(p["startup_idle_timeout_ms"]); ok {
prof.StartupIdleTimeoutMS = v
}
if v, ok := p["output_format"].(string); ok {
prof.OutputFormat = v
}
cfg.Profiles[name] = prof
}
}
return cfg
}
func boolFromAny(v any) (bool, bool) {
if v == nil {
return false, false
}
if b, ok := v.(bool); ok {
return b, true
}
return false, false
}
func intFromAny(v any) (int, bool) {
if v == nil {
return 0, false
}
// structpb encodes numbers as float64
if f, ok := v.(float64); ok {
return int(f), true
}
if i, ok := v.(int); ok {
return i, true
}
return 0, false
}