blob: ea7474dd9a9d9080028466e840cf6a1e1c1b0cdc [file] [log] [blame]
package agent
import (
"fmt"
"log/slog"
"time"
"github.com/iomodo/staff/config"
"github.com/iomodo/staff/llm"
_ "github.com/iomodo/staff/llm/providers" // Auto-register all providers
"github.com/iomodo/staff/task"
"github.com/iomodo/staff/tm"
)
// Manager manages multiple AI agents with Git operations and task processing
type Manager struct {
config *config.Config
agents map[string]*Agent
taskManager tm.TaskManager
autoAssigner *task.AutoAssigner
isRunning map[string]bool
roles []string
logger *slog.Logger
}
// NewManager creates a new agent manager
func NewManager(cfg *config.Config, taskManager tm.TaskManager, logger *slog.Logger) (*Manager, error) {
autoAssigner := task.NewAutoAssigner(cfg.Agents)
agentRoles := make([]string, 0, len(cfg.Agents))
for _, agentConfig := range cfg.Agents {
agentRoles = append(agentRoles, agentConfig.Role)
}
manager := &Manager{
config: cfg,
agents: make(map[string]*Agent),
taskManager: taskManager,
autoAssigner: autoAssigner,
isRunning: make(map[string]bool),
roles: agentRoles,
logger: logger,
}
// Initialize agents
if err := manager.initializeAgents(); err != nil {
return nil, fmt.Errorf("failed to initialize agents: %w", err)
}
return manager, nil
}
// initializeAgents creates agent instances from configuration
func (m *Manager) initializeAgents() error {
llmConfig := llm.Config{
Provider: llm.ProviderFake, // Use fake provider for testing
APIKey: m.config.OpenAI.APIKey,
BaseURL: m.config.OpenAI.BaseURL,
Timeout: m.config.OpenAI.Timeout,
}
for _, agentConfig := range m.config.Agents {
agent, err := NewAgent(agentConfig, llmConfig, m.taskManager, m.roles, m.logger)
if err != nil {
return fmt.Errorf("failed to create agent %s: %w", agentConfig.Name, err)
}
m.agents[agentConfig.Name] = agent
}
return nil
}
func (m *Manager) StartAllAgents() {
// Start all configured agents with a default loop interval
defaultInterval := 1 * time.Second
for _, a := range m.agents {
m.logger.Info("Starting agent",
slog.String("name", a.Name),
slog.String("role", a.Role),
slog.String("model", a.Model))
if err := a.Start(defaultInterval); err != nil {
m.logger.Error("Failed to start agent",
slog.String("agent", a.Name),
slog.String("error", err.Error()))
continue
}
m.isRunning[a.Name] = true
}
}
func (m *Manager) StartAgent(agentName string, loopInterval time.Duration) error {
agent, exists := m.agents[agentName]
if !exists {
return fmt.Errorf("agent %s not found", agentName)
}
if m.isRunning[agentName] {
return fmt.Errorf("agent %s is already running", agentName)
}
agent.Start(loopInterval)
m.isRunning[agentName] = true
return nil
}
// StopAgent stops a running agent
func (m *Manager) StopAgent(agentName string) error {
agent, exists := m.agents[agentName]
if !exists {
return fmt.Errorf("agent %s not found", agentName)
}
if !m.isRunning[agentName] {
return fmt.Errorf("agent %s is not running", agentName)
}
agent.Stop()
m.isRunning[agentName] = false
m.logger.Info("Stopped agent", slog.String("name", agentName))
return nil
}
// AutoAssignTask automatically assigns a task to the best matching agent
func (m *Manager) AutoAssignTask(taskID string) error {
task, err := m.taskManager.GetTask(taskID)
if err != nil {
return fmt.Errorf("failed to get task: %w", err)
}
agentName, err := m.autoAssigner.AssignTask(task)
if err != nil {
return fmt.Errorf("failed to auto-assign task: %w", err)
}
task.Assignee = agentName
if err := m.taskManager.UpdateTask(task); err != nil {
return fmt.Errorf("failed to update task assignment: %w", err)
}
explanation := m.autoAssigner.GetRecommendationExplanation(task, agentName)
m.logger.Info("Auto-assigned task to agent",
slog.String("task_id", taskID),
slog.String("agent", agentName),
slog.String("explanation", explanation))
return nil
}
// IsAgentRunning checks if an agent is currently running
func (m *Manager) IsAgentRunning(agentName string) bool {
return m.isRunning[agentName]
}
// Close shuts down the agent manager
func (m *Manager) Close() error {
// Stop all running agents
for agentName := range m.isRunning {
if m.isRunning[agentName] {
m.StopAgent(agentName)
}
}
// Close all LLM providers
for _, agent := range m.agents {
if err := agent.Provider.Close(); err != nil {
m.logger.Error("Error closing provider for agent",
slog.String("agent", agent.Name),
slog.String("error", err.Error()))
}
}
return nil
}