Refactor subtasks
Change-Id: I5ea6ffe266b8d6010de46bbf3bc6d7f861600f00
diff --git a/server/agent/manager.go b/server/agent/manager.go
index c206665..a302133 100644
--- a/server/agent/manager.go
+++ b/server/agent/manager.go
@@ -10,27 +10,26 @@
"strings"
"time"
- "github.com/iomodo/staff/assignment"
"github.com/iomodo/staff/config"
"github.com/iomodo/staff/git"
"github.com/iomodo/staff/llm"
_ "github.com/iomodo/staff/llm/providers" // Auto-register all providers
- "github.com/iomodo/staff/subtasks"
+ "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 *assignment.AutoAssigner
- prProvider git.PullRequestProvider
- cloneManager *git.CloneManager
- subtaskService *subtasks.SubtaskService
- isRunning map[string]bool
- stopChannels map[string]chan struct{}
- logger *slog.Logger
+ config *config.Config
+ agents map[string]*Agent
+ taskManager tm.TaskManager
+ autoAssigner *task.AutoAssigner
+ prProvider git.PullRequestProvider
+ cloneManager *git.CloneManager
+ subtaskService *task.SubtaskService
+ isRunning map[string]bool
+ stopChannels map[string]chan struct{}
+ logger *slog.Logger
}
// NewManager creates a new agent manager
@@ -39,12 +38,12 @@
logger = slog.Default()
}
// Create auto-assigner
- autoAssigner := assignment.NewAutoAssigner(cfg.Agents)
+ autoAssigner := task.NewAutoAssigner(cfg.Agents)
// Create PR provider based on configuration
var prProvider git.PullRequestProvider
var repoURL string
-
+
switch cfg.GetPrimaryGitProvider() {
case "github":
githubConfig := git.GitHubConfig{
@@ -53,8 +52,8 @@
}
prProvider = git.NewGitHubPullRequestProvider(cfg.GitHub.Owner, cfg.GitHub.Repo, githubConfig)
repoURL = fmt.Sprintf("https://github.com/%s/%s.git", cfg.GitHub.Owner, cfg.GitHub.Repo)
- logger.Info("Using GitHub as pull request provider",
- slog.String("owner", cfg.GitHub.Owner),
+ logger.Info("Using GitHub as pull request provider",
+ slog.String("owner", cfg.GitHub.Owner),
slog.String("repo", cfg.GitHub.Repo))
case "gerrit":
gerritConfig := git.GerritConfig{
@@ -65,14 +64,14 @@
}
prProvider = git.NewGerritPullRequestProvider(cfg.Gerrit.Project, gerritConfig)
repoURL = fmt.Sprintf("%s/%s", cfg.Gerrit.BaseURL, cfg.Gerrit.Project)
- logger.Info("Using Gerrit as pull request provider",
- slog.String("base_url", cfg.Gerrit.BaseURL),
+ logger.Info("Using Gerrit as pull request provider",
+ slog.String("base_url", cfg.Gerrit.BaseURL),
slog.String("project", cfg.Gerrit.Project))
default:
return nil, fmt.Errorf("no valid Git provider configured")
}
- // Create clone manager for per-agent Git repositories
+ // Create clone manager for per-agent Git repositories
workspacePath := filepath.Join(".", "workspace")
cloneManager := git.NewCloneManager(repoURL, workspacePath)
@@ -142,8 +141,8 @@
owner = m.config.Gerrit.Project
repo = m.config.Gerrit.Project
}
-
- m.subtaskService = subtasks.NewSubtaskService(
+
+ m.subtaskService = task.NewSubtaskService(
firstAgent.Provider,
m.taskManager,
agentRoles,
@@ -218,9 +217,9 @@
go m.runAgentLoop(agent, loopInterval, stopChan)
- m.logger.Info("Started agent",
- slog.String("name", agentName),
- slog.String("role", agent.Role),
+ m.logger.Info("Started agent",
+ slog.String("name", agentName),
+ slog.String("role", agent.Role),
slog.String("model", agent.Model))
return nil
}
@@ -251,8 +250,8 @@
return
case <-ticker.C:
if err := m.processAgentTasks(agent); err != nil {
- m.logger.Error("Error processing tasks for agent",
- slog.String("agent", agent.Name),
+ m.logger.Error("Error processing tasks for agent",
+ slog.String("agent", agent.Name),
slog.String("error", err.Error()))
}
}
@@ -271,21 +270,21 @@
return fmt.Errorf("failed to get tasks for agent %s: %w", agent.Name, err)
}
- m.logger.Info("Processing tasks for agent",
- slog.Int("task_count", len(tasks)),
+ m.logger.Info("Processing tasks for agent",
+ slog.Int("task_count", len(tasks)),
slog.String("agent", agent.Name))
for _, task := range tasks {
if task.Status == tm.StatusToDo || task.Status == tm.StatusPending {
if err := m.processTask(agent, task); err != nil {
- m.logger.Error("Error processing task",
- slog.String("task_id", task.ID),
+ m.logger.Error("Error processing task",
+ slog.String("task_id", task.ID),
slog.String("error", err.Error()))
// Mark task as failed
task.Status = tm.StatusFailed
if err := m.taskManager.UpdateTask(task); err != nil {
- m.logger.Error("Error updating failed task",
- slog.String("task_id", task.ID),
+ m.logger.Error("Error updating failed task",
+ slog.String("task_id", task.ID),
slog.String("error", err.Error()))
}
agent.Stats.TasksFailed++
@@ -308,9 +307,9 @@
ctx := context.Background()
startTime := time.Now()
- m.logger.Info("Agent processing task",
- slog.String("agent", agent.Name),
- slog.String("task_id", task.ID),
+ m.logger.Info("Agent processing task",
+ slog.String("agent", agent.Name),
+ slog.String("task_id", task.ID),
slog.String("title", task.Title))
// Mark task as in progress
@@ -324,8 +323,8 @@
if m.shouldGenerateSubtasks(task) {
m.logger.Info("LLM determined task should generate subtasks", slog.String("task_id", task.ID))
if err := m.generateSubtasksForTask(ctx, task); err != nil {
- m.logger.Warn("Failed to generate subtasks for task",
- slog.String("task_id", task.ID),
+ m.logger.Warn("Failed to generate subtasks for task",
+ slog.String("task_id", task.ID),
slog.String("error", err.Error()))
// Continue with normal processing if subtask generation fails
} else {
@@ -340,8 +339,8 @@
return fmt.Errorf("failed to update task with subtasks: %w", err)
}
- m.logger.Info("Task converted to subtasks by agent using LLM analysis",
- slog.String("task_id", task.ID),
+ m.logger.Info("Task converted to subtasks by agent using LLM analysis",
+ slog.String("task_id", task.ID),
slog.String("agent", agent.Name))
return nil
}
@@ -385,10 +384,10 @@
agent.Stats.AvgTime = (agent.Stats.AvgTime + duration.Milliseconds()) / 2
}
- m.logger.Info("Task completed by agent",
- slog.String("task_id", task.ID),
- slog.String("agent", agent.Name),
- slog.Duration("duration", duration),
+ m.logger.Info("Task completed by agent",
+ slog.String("task_id", task.ID),
+ slog.String("agent", agent.Name),
+ slog.Duration("duration", duration),
slog.String("pr_url", prURL))
return nil
}
@@ -478,14 +477,14 @@
return fmt.Errorf("failed to get agent clone: %w", err)
}
- m.logger.Info("Agent working in clone",
- slog.String("agent", agent.Name),
+ m.logger.Info("Agent working in clone",
+ slog.String("agent", agent.Name),
slog.String("clone_path", clonePath))
// Refresh the clone with latest changes
if err := m.cloneManager.RefreshAgentClone(agent.Name); err != nil {
- m.logger.Warn("Failed to refresh clone for agent",
- slog.String("agent", agent.Name),
+ m.logger.Warn("Failed to refresh clone for agent",
+ slog.String("agent", agent.Name),
slog.String("error", err.Error()))
}
@@ -557,8 +556,8 @@
return fmt.Errorf("failed to push branch: %w", err)
}
- m.logger.Info("Agent successfully pushed branch",
- slog.String("agent", agent.Name),
+ m.logger.Info("Agent successfully pushed branch",
+ slog.String("agent", agent.Name),
slog.String("branch", branchName))
return nil
}
@@ -660,9 +659,9 @@
}
explanation := m.autoAssigner.GetRecommendationExplanation(task, agentName)
- m.logger.Info("Auto-assigned task to agent",
- slog.String("task_id", taskID),
- slog.String("agent", agentName),
+ m.logger.Info("Auto-assigned task to agent",
+ slog.String("task_id", taskID),
+ slog.String("agent", agentName),
slog.String("explanation", explanation))
return nil
@@ -711,8 +710,8 @@
ctx := context.Background()
decision, err := m.subtaskService.ShouldGenerateSubtasks(ctx, task)
if err != nil {
- m.logger.Warn("Failed to get LLM subtask decision for task",
- slog.String("task_id", task.ID),
+ m.logger.Warn("Failed to get LLM subtask decision for task",
+ slog.String("task_id", task.ID),
slog.String("error", err.Error()))
// Fallback to simple heuristics
return task.Priority == tm.PriorityHigh || len(task.Description) > 200
@@ -724,10 +723,10 @@
m.logger.Warn("Failed to update task evaluation status", slog.String("error", err.Error()))
}
- m.logger.Info("LLM subtask decision for task",
- slog.String("task_id", task.ID),
- slog.Bool("needs_subtasks", decision.NeedsSubtasks),
- slog.Int("complexity_score", decision.ComplexityScore),
+ m.logger.Info("LLM subtask decision for task",
+ slog.String("task_id", task.ID),
+ slog.Bool("needs_subtasks", decision.NeedsSubtasks),
+ slog.Int("complexity_score", decision.ComplexityScore),
slog.String("reasoning", decision.Reasoning))
return decision.NeedsSubtasks
@@ -755,19 +754,19 @@
task.SubtasksPRURL = prURL
task.SubtasksGenerated = true
- m.logger.Info("Generated subtask PR for task",
- slog.String("task_id", task.ID),
+ m.logger.Info("Generated subtask PR for task",
+ slog.String("task_id", task.ID),
slog.String("pr_url", prURL))
- m.logger.Info("Proposed subtasks and new agents for task",
- slog.String("task_id", task.ID),
- slog.Int("subtask_count", len(analysis.Subtasks)),
+ m.logger.Info("Proposed subtasks and new agents for task",
+ slog.String("task_id", task.ID),
+ slog.Int("subtask_count", len(analysis.Subtasks)),
slog.Int("new_agent_count", len(analysis.AgentCreations)))
-
+
// Log proposed new agents if any
if len(analysis.AgentCreations) > 0 {
for _, agent := range analysis.AgentCreations {
- m.logger.Info("Proposed new agent",
- slog.String("role", agent.Role),
+ m.logger.Info("Proposed new agent",
+ slog.String("role", agent.Role),
slog.Any("skills", agent.Skills))
}
}
@@ -792,8 +791,8 @@
// 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),
+ m.logger.Error("Error closing provider for agent",
+ slog.String("agent", agent.Name),
slog.String("error", err.Error()))
}
}