2667af52cc
Implementa el sistema de orquestación para salas Matrix con múltiples bots. El orquestador es un "special agent" sin identidad Matrix que coordina qué bot responde y cuándo, usando LLM (Claude) para routing y evaluación de calidad. Cambios principales: - pkg/orchestration/task.go: tipos puros (TaskEvent, BotResponse, QualityScore, RoutingDecision) - shell/orchestration/: runtime del orquestador (orchestrator.go, router.go, evaluator.go) - agents/specials/orchestrator/: config + prompts (routing, quality, refinement) - internal/config/: SpecialConfig, OrchestrationCfg, LoadSpecial() - shell/bus/bus.go: protocolo request-reply (SendAndWait, Reply) para delegación - shell/matrix/listener.go: InterceptFunc para interceptar eventos en salas orquestadas - agents/runtime.go: SetBus, listenBus, handleTaskEvent para recibir tareas del orquestador - cmd/launcher/main.go: creación de bus compartido, arranque del orquestador antes de bots Incluye deduplicación para evitar que múltiples listeners en la misma sala disparen el orquestador más de una vez por mensaje. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
2.7 KiB
Go
102 lines
2.7 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Load reads and parses an agent config file from the given path.
|
|
func Load(path string) (*AgentConfig, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read config %s: %w", path, err)
|
|
}
|
|
|
|
// Expand environment variables in the raw YAML bytes.
|
|
expanded := os.ExpandEnv(string(data))
|
|
|
|
var cfg AgentConfig
|
|
if err := yaml.Unmarshal([]byte(expanded), &cfg); err != nil {
|
|
return nil, fmt.Errorf("parse config %s: %w", path, err)
|
|
}
|
|
|
|
if err := validate(&cfg); err != nil {
|
|
return nil, fmt.Errorf("invalid config %s: %w", path, err)
|
|
}
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
// LoadMeta reads only the `agent:` block from a config file without expanding
|
|
// env vars or running full validation. Used by agentctl list to show all
|
|
// agents regardless of whether their env vars are configured.
|
|
func LoadMeta(path string) (*AgentConfig, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read config %s: %w", path, err)
|
|
}
|
|
var cfg AgentConfig
|
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parse config %s: %w", path, err)
|
|
}
|
|
if cfg.Agent.ID == "" {
|
|
return nil, fmt.Errorf("agent.id is required")
|
|
}
|
|
return &cfg, nil
|
|
}
|
|
|
|
// LoadSpecial reads and parses a special agent config file.
|
|
// Special agents have no Matrix identity so validation is lighter.
|
|
func LoadSpecial(path string) (*SpecialConfig, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read special config %s: %w", path, err)
|
|
}
|
|
|
|
expanded := os.ExpandEnv(string(data))
|
|
|
|
var cfg SpecialConfig
|
|
if err := yaml.Unmarshal([]byte(expanded), &cfg); err != nil {
|
|
return nil, fmt.Errorf("parse special config %s: %w", path, err)
|
|
}
|
|
|
|
if err := validateSpecial(&cfg); err != nil {
|
|
return nil, fmt.Errorf("invalid special config %s: %w", path, err)
|
|
}
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
// validateSpecial applies sanity checks for special agent configs.
|
|
func validateSpecial(cfg *SpecialConfig) error {
|
|
if cfg.Special.ID == "" {
|
|
return fmt.Errorf("special.id is required")
|
|
}
|
|
if cfg.Special.Type == "" {
|
|
return fmt.Errorf("special.type is required")
|
|
}
|
|
if cfg.LLM.Primary.Provider == "" {
|
|
return fmt.Errorf("llm.primary.provider is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// validate applies basic sanity checks.
|
|
func validate(cfg *AgentConfig) error {
|
|
if cfg.Agent.ID == "" {
|
|
return fmt.Errorf("agent.id is required")
|
|
}
|
|
if cfg.Matrix.Homeserver == "" {
|
|
return fmt.Errorf("matrix.homeserver is required")
|
|
}
|
|
if cfg.Matrix.UserID == "" {
|
|
return fmt.Errorf("matrix.user_id is required")
|
|
}
|
|
if cfg.LLM.Primary.Provider == "" {
|
|
return fmt.Errorf("llm.primary.provider is required")
|
|
}
|
|
return nil
|
|
}
|