- Project infrastructure: go.mod, Makefile, .gitignore, README - Configuration system with YAML parsing and env overrides - Structured logging with slog - AI provider interface with Ollama, OpenAI, Anthropic implementations - Provider manager with runtime switching - Agent orchestrator with ReAct loop (reason-act-observe) - Tool registry with JSON Schema descriptions - Terminal tool: shell command execution with safety controls - Filesystem tools: read, write, list with path access control - Conversation memory with session management - Web UI server with WebSocket streaming - Modern dark theme UI with glassmorphism, animations - Frontend: WebSocket client, markdown rendering, tool call display
83 lines
1.8 KiB
Go
83 lines
1.8 KiB
Go
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
|
|
}
|