package ai import ( "fmt" "log/slog" "sync" ) // Manager manages multiple AI providers and routes requests. type Manager struct { mu sync.RWMutex providers map[string]Provider active string log *slog.Logger } // NewManager creates a new provider manager. func NewManager(log *slog.Logger) *Manager { return &Manager{ providers: make(map[string]Provider), log: log, } } // Register adds a provider to the manager. func (m *Manager) Register(p Provider) { m.mu.Lock() defer m.mu.Unlock() m.providers[p.Name()] = p m.log.Info("registered AI provider", "name", p.Name(), "models", p.Models()) } // SetActive sets the active provider by name. func (m *Manager) SetActive(name string) error { m.mu.Lock() defer m.mu.Unlock() if _, ok := m.providers[name]; !ok { return fmt.Errorf("unknown provider: %s", name) } m.active = name m.log.Info("switched active provider", "name", name) return nil } // Active returns the currently active provider. func (m *Manager) Active() (Provider, error) { m.mu.RLock() defer m.mu.RUnlock() p, ok := m.providers[m.active] if !ok { return nil, fmt.Errorf("no active provider set (active=%q)", m.active) } return p, nil } // ActiveName returns the name of the currently active provider. func (m *Manager) ActiveName() string { m.mu.RLock() defer m.mu.RUnlock() return m.active } // List returns names of all registered providers. func (m *Manager) List() []string { m.mu.RLock() defer m.mu.RUnlock() names := make([]string, 0, len(m.providers)) for name := range m.providers { names = append(names, name) } return names } // Get returns a provider by name. func (m *Manager) Get(name string) (Provider, error) { m.mu.RLock() defer m.mu.RUnlock() p, ok := m.providers[name] if !ok { return nil, fmt.Errorf("unknown provider: %s", name) } return p, nil }