 5978a0b8f5
			
		
	
	5978a0b8f5
	
	
	
		
			
			- Agent roles and coordination features - Chat API integration testing - New configuration and workspace management 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			334 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			334 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package repository
 | |
| 
 | |
| import (
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // DefaultTaskMatcher implements TaskMatcher interface
 | |
| type DefaultTaskMatcher struct{}
 | |
| 
 | |
| // MatchTasksToRole filters tasks suitable for a specific role and expertise
 | |
| func (m *DefaultTaskMatcher) MatchTasksToRole(tasks []*Task, role string, expertise []string) ([]*Task, error) {
 | |
| 	var matchedTasks []*Task
 | |
| 	
 | |
| 	for _, task := range tasks {
 | |
| 		score := m.ScoreTaskForAgent(task, role, expertise)
 | |
| 		if score > 0.3 { // Minimum threshold for task suitability
 | |
| 			matchedTasks = append(matchedTasks, task)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Sort by score (highest first)
 | |
| 	sort.Slice(matchedTasks, func(i, j int) bool {
 | |
| 		scoreI := m.ScoreTaskForAgent(matchedTasks[i], role, expertise)
 | |
| 		scoreJ := m.ScoreTaskForAgent(matchedTasks[j], role, expertise)
 | |
| 		return scoreI > scoreJ
 | |
| 	})
 | |
| 	
 | |
| 	return matchedTasks, nil
 | |
| }
 | |
| 
 | |
| // ScoreTaskForAgent calculates how suitable a task is for an agent based on role and expertise
 | |
| func (m *DefaultTaskMatcher) ScoreTaskForAgent(task *Task, agentRole string, agentExpertise []string) float64 {
 | |
| 	score := 0.0
 | |
| 	
 | |
| 	// Base score for role matching
 | |
| 	if task.RequiredRole != "" {
 | |
| 		if task.RequiredRole == agentRole {
 | |
| 			score += 0.5 // Perfect role match
 | |
| 		} else if m.isCompatibleRole(task.RequiredRole, agentRole) {
 | |
| 			score += 0.3 // Compatible role
 | |
| 		}
 | |
| 	} else {
 | |
| 		// No specific role required, default bonus for general roles
 | |
| 		if m.isGeneralRole(agentRole) {
 | |
| 			score += 0.2
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Expertise matching
 | |
| 	expertiseScore := m.calculateExpertiseScore(task.RequiredExpertise, agentExpertise)
 | |
| 	score += expertiseScore * 0.4
 | |
| 	
 | |
| 	// Priority bonus (higher priority tasks get small bonus)
 | |
| 	priorityBonus := float64(task.Priority) / 10.0 * 0.1
 | |
| 	score += priorityBonus
 | |
| 	
 | |
| 	// Task type bonuses based on agent role
 | |
| 	taskTypeScore := m.calculateTaskTypeScore(task, agentRole)
 | |
| 	score += taskTypeScore * 0.1
 | |
| 	
 | |
| 	// Label-based scoring
 | |
| 	labelScore := m.calculateLabelScore(task.Labels, agentRole, agentExpertise)
 | |
| 	score += labelScore * 0.1
 | |
| 	
 | |
| 	// Ensure score is between 0 and 1
 | |
| 	if score > 1.0 {
 | |
| 		score = 1.0
 | |
| 	}
 | |
| 	if score < 0.0 {
 | |
| 		score = 0.0
 | |
| 	}
 | |
| 	
 | |
| 	return score
 | |
| }
 | |
| 
 | |
| // GetRecommendedAgents returns agents best suited for a task
 | |
| func (m *DefaultTaskMatcher) GetRecommendedAgents(task *Task, availableAgents []AgentInfo) ([]AgentInfo, error) {
 | |
| 	type agentScore struct {
 | |
| 		agent AgentInfo
 | |
| 		score float64
 | |
| 	}
 | |
| 	
 | |
| 	var scoredAgents []agentScore
 | |
| 	
 | |
| 	for _, agent := range availableAgents {
 | |
| 		// Skip agents that are offline or at capacity
 | |
| 		if agent.Status != "online" && agent.Status != "ready" {
 | |
| 			continue
 | |
| 		}
 | |
| 		if agent.CurrentTasks >= agent.MaxTasks {
 | |
| 			continue
 | |
| 		}
 | |
| 		
 | |
| 		// Calculate base task suitability score
 | |
| 		taskScore := m.ScoreTaskForAgent(task, agent.Role, agent.Expertise)
 | |
| 		
 | |
| 		// Apply agent-specific modifiers
 | |
| 		finalScore := taskScore
 | |
| 		
 | |
| 		// Performance modifier (0.5 to 1.5 multiplier)
 | |
| 		performanceMod := 0.5 + agent.Performance
 | |
| 		finalScore *= performanceMod
 | |
| 		
 | |
| 		// Availability modifier
 | |
| 		availabilityMod := agent.Availability
 | |
| 		finalScore *= availabilityMod
 | |
| 		
 | |
| 		// Workload penalty (agents with fewer current tasks get slight bonus)
 | |
| 		workloadRatio := float64(agent.CurrentTasks) / float64(agent.MaxTasks)
 | |
| 		workloadBonus := (1.0 - workloadRatio) * 0.1
 | |
| 		finalScore += workloadBonus
 | |
| 		
 | |
| 		// Recent activity bonus (agents seen recently get small bonus)
 | |
| 		timeSinceLastSeen := time.Since(agent.LastSeen)
 | |
| 		if timeSinceLastSeen < 5*time.Minute {
 | |
| 			finalScore += 0.05
 | |
| 		}
 | |
| 		
 | |
| 		if finalScore > 0.1 { // Minimum threshold
 | |
| 			scoredAgents = append(scoredAgents, agentScore{agent: agent, score: finalScore})
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Sort by score (highest first)
 | |
| 	sort.Slice(scoredAgents, func(i, j int) bool {
 | |
| 		return scoredAgents[i].score > scoredAgents[j].score
 | |
| 	})
 | |
| 	
 | |
| 	// Return top agents (up to 5)
 | |
| 	maxAgents := 5
 | |
| 	if len(scoredAgents) < maxAgents {
 | |
| 		maxAgents = len(scoredAgents)
 | |
| 	}
 | |
| 	
 | |
| 	result := make([]AgentInfo, maxAgents)
 | |
| 	for i := 0; i < maxAgents; i++ {
 | |
| 		result[i] = scoredAgents[i].agent
 | |
| 	}
 | |
| 	
 | |
| 	return result, nil
 | |
| }
 | |
| 
 | |
| // calculateExpertiseScore computes overlap between required and agent expertise
 | |
| func (m *DefaultTaskMatcher) calculateExpertiseScore(requiredExpertise, agentExpertise []string) float64 {
 | |
| 	if len(requiredExpertise) == 0 {
 | |
| 		return 0.5 // No specific expertise required
 | |
| 	}
 | |
| 	
 | |
| 	matches := 0
 | |
| 	for _, required := range requiredExpertise {
 | |
| 		for _, agent := range agentExpertise {
 | |
| 			if strings.EqualFold(required, agent) || m.isRelatedExpertise(required, agent) {
 | |
| 				matches++
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Score based on percentage of required expertise covered
 | |
| 	score := float64(matches) / float64(len(requiredExpertise))
 | |
| 	
 | |
| 	// Bonus for having additional relevant expertise
 | |
| 	if len(agentExpertise) > len(requiredExpertise) {
 | |
| 		bonus := 0.1 * float64(len(agentExpertise)-len(requiredExpertise)) / float64(len(agentExpertise))
 | |
| 		score += bonus
 | |
| 	}
 | |
| 	
 | |
| 	return score
 | |
| }
 | |
| 
 | |
| // calculateTaskTypeScore gives bonuses based on task type and agent role compatibility
 | |
| func (m *DefaultTaskMatcher) calculateTaskTypeScore(task *Task, agentRole string) float64 {
 | |
| 	taskType := strings.ToLower(task.TaskType)
 | |
| 	role := strings.ToLower(agentRole)
 | |
| 	
 | |
| 	switch taskType {
 | |
| 	case "bug_fix", "bug":
 | |
| 		if strings.Contains(role, "qa") || strings.Contains(role, "test") {
 | |
| 			return 0.8
 | |
| 		}
 | |
| 		if strings.Contains(role, "backend") || strings.Contains(role, "full_stack") {
 | |
| 			return 0.6
 | |
| 		}
 | |
| 	case "feature", "enhancement":
 | |
| 		if strings.Contains(role, "developer") || strings.Contains(role, "engineer") {
 | |
| 			return 0.7
 | |
| 		}
 | |
| 	case "documentation", "docs":
 | |
| 		if strings.Contains(role, "writer") || strings.Contains(role, "documentation") {
 | |
| 			return 0.9
 | |
| 		}
 | |
| 	case "security":
 | |
| 		if strings.Contains(role, "security") {
 | |
| 			return 0.9
 | |
| 		}
 | |
| 	case "design":
 | |
| 		if strings.Contains(role, "designer") || strings.Contains(role, "ux") {
 | |
| 			return 0.9
 | |
| 		}
 | |
| 	case "infrastructure", "devops":
 | |
| 		if strings.Contains(role, "devops") || strings.Contains(role, "systems") {
 | |
| 			return 0.9
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	return 0.0
 | |
| }
 | |
| 
 | |
| // calculateLabelScore analyzes task labels for additional role matching
 | |
| func (m *DefaultTaskMatcher) calculateLabelScore(labels []string, agentRole string, agentExpertise []string) float64 {
 | |
| 	score := 0.0
 | |
| 	role := strings.ToLower(agentRole)
 | |
| 	
 | |
| 	for _, label := range labels {
 | |
| 		label = strings.ToLower(label)
 | |
| 		
 | |
| 		// Direct role matches
 | |
| 		if strings.Contains(role, label) || strings.Contains(label, role) {
 | |
| 			score += 0.3
 | |
| 		}
 | |
| 		
 | |
| 		// Expertise matches
 | |
| 		for _, expertise := range agentExpertise {
 | |
| 			if strings.Contains(strings.ToLower(expertise), label) || strings.Contains(label, strings.ToLower(expertise)) {
 | |
| 				score += 0.2
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		// Specific label bonuses
 | |
| 		switch label {
 | |
| 		case "frontend":
 | |
| 			if strings.Contains(role, "frontend") || strings.Contains(role, "ui") {
 | |
| 				score += 0.4
 | |
| 			}
 | |
| 		case "backend":
 | |
| 			if strings.Contains(role, "backend") || strings.Contains(role, "api") {
 | |
| 				score += 0.4
 | |
| 			}
 | |
| 		case "urgent", "critical":
 | |
| 			score += 0.1 // Small bonus for urgent tasks
 | |
| 		case "good first issue", "beginner":
 | |
| 			if strings.Contains(role, "junior") {
 | |
| 				score += 0.3
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	return score
 | |
| }
 | |
| 
 | |
| // isCompatibleRole checks if two roles are compatible
 | |
| func (m *DefaultTaskMatcher) isCompatibleRole(requiredRole, agentRole string) bool {
 | |
| 	compatibilityMap := map[string][]string{
 | |
| 		"frontend_developer":      {"full_stack_engineer", "ui_ux_designer"},
 | |
| 		"backend_developer":       {"full_stack_engineer", "database_engineer"},
 | |
| 		"full_stack_engineer":     {"frontend_developer", "backend_developer"},
 | |
| 		"qa_engineer":             {"full_stack_engineer", "backend_developer"},
 | |
| 		"devops_engineer":         {"systems_engineer", "backend_developer"},
 | |
| 		"ui_ux_designer":          {"frontend_developer", "lead_designer"},
 | |
| 		"security_expert":         {"backend_developer", "senior_software_architect"},
 | |
| 		"technical_writer":        {"full_stack_engineer"},
 | |
| 		"database_engineer":       {"backend_developer", "full_stack_engineer"},
 | |
| 		"senior_software_architect": {"full_stack_engineer", "backend_developer", "frontend_developer"},
 | |
| 	}
 | |
| 	
 | |
| 	compatibleRoles, exists := compatibilityMap[requiredRole]
 | |
| 	if !exists {
 | |
| 		return false
 | |
| 	}
 | |
| 	
 | |
| 	for _, compatible := range compatibleRoles {
 | |
| 		if compatible == agentRole {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // isGeneralRole checks if a role is considered general-purpose
 | |
| func (m *DefaultTaskMatcher) isGeneralRole(role string) bool {
 | |
| 	generalRoles := []string{
 | |
| 		"full_stack_engineer",
 | |
| 		"senior_software_architect",
 | |
| 		"general_developer",
 | |
| 	}
 | |
| 	
 | |
| 	for _, general := range generalRoles {
 | |
| 		if general == role {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // isRelatedExpertise checks if two expertise areas are related
 | |
| func (m *DefaultTaskMatcher) isRelatedExpertise(required, agent string) bool {
 | |
| 	relatedMap := map[string][]string{
 | |
| 		"frontend":        {"javascript", "typescript", "react", "vue", "angular", "css", "html"},
 | |
| 		"backend":         {"api_development", "server_frameworks", "databases", "microservices"},
 | |
| 		"database":        {"sql", "nosql", "data_modeling", "query_optimization"},
 | |
| 		"security":        {"cybersecurity", "owasp", "vulnerability_analysis", "penetration_testing"},
 | |
| 		"devops":          {"deployment", "infrastructure", "docker", "kubernetes", "cicd"},
 | |
| 		"testing":         {"qa_methodologies", "test_automation", "debugging"},
 | |
| 		"design":          {"ui_ux", "user_experience", "prototyping", "design_systems"},
 | |
| 		"documentation":   {"technical_writing", "api_documentation"},
 | |
| 	}
 | |
| 	
 | |
| 	required = strings.ToLower(required)
 | |
| 	agent = strings.ToLower(agent)
 | |
| 	
 | |
| 	// Check direct related expertise
 | |
| 	if related, exists := relatedMap[required]; exists {
 | |
| 		for _, rel := range related {
 | |
| 			if strings.Contains(agent, rel) || strings.Contains(rel, agent) {
 | |
| 				return true
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Check reverse mapping
 | |
| 	if related, exists := relatedMap[agent]; exists {
 | |
| 		for _, rel := range related {
 | |
| 			if strings.Contains(required, rel) || strings.Contains(rel, required) {
 | |
| 				return true
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	return false
 | |
| } |