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 }