feat: import agents_and_robots platform as unibots (Matrix-out, unibus transport)
Reemplaza el scaffold del echobot por la plataforma completa de bots traida desde ~/DataProyects/Github/agents_and_robots tras la operacion Matrix-out: los bots ya no hablan por Matrix sino por el bus unibus (modelo todo-rooms + E2E via shell/transportunibus sobre github.com/enmanuel/unibus/pkg/client). - go.mod: replace de unibus -> ../unibus y de fn-registry -> ../../../.. (paths relativos reajustados a la nueva ubicacion dentro de fn_registry). - app.md: bump a 0.2.0, descripcion + arquitectura + comandos + gotchas reales. - modulo Go conservado como github.com/enmanuel/agents (sin reescribir imports). agents_and_robots queda archivado como museo de la era Matrix.
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// TestAgentConfigParseMinimal verifies that a minimal config YAML (with only
|
||||
// required fields) parses into AgentConfig without error.
|
||||
func TestAgentConfigParseMinimal(t *testing.T) {
|
||||
const minimalYAML = `
|
||||
agent:
|
||||
id: test-bot
|
||||
name: Test Bot
|
||||
enabled: true
|
||||
bus:
|
||||
nats_url: "nats://127.0.0.1:4250"
|
||||
ctrl_url: "http://127.0.0.1:8470"
|
||||
llm:
|
||||
primary:
|
||||
provider: openai
|
||||
model: gpt-4o
|
||||
`
|
||||
var cfg AgentConfig
|
||||
if err := yaml.Unmarshal([]byte(minimalYAML), &cfg); err != nil {
|
||||
t.Fatalf("failed to parse minimal config: %v", err)
|
||||
}
|
||||
if cfg.Agent.ID != "test-bot" {
|
||||
t.Errorf("expected agent.id=test-bot, got %q", cfg.Agent.ID)
|
||||
}
|
||||
if cfg.Bus.NatsURL != "nats://127.0.0.1:4250" {
|
||||
t.Errorf("expected bus.nats_url, got %q", cfg.Bus.NatsURL)
|
||||
}
|
||||
if cfg.LLM.Primary.Provider != "openai" {
|
||||
t.Errorf("expected provider=openai, got %q", cfg.LLM.Primary.Provider)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAgentConfigIgnoresRemovedSections verifies that YAML containing the
|
||||
// removed sections (agents, observability, resilience) still parses without
|
||||
// error. yaml.v3 silently ignores unknown keys.
|
||||
func TestAgentConfigIgnoresRemovedSections(t *testing.T) {
|
||||
const yamlWithRemoved = `
|
||||
agent:
|
||||
id: legacy-bot
|
||||
name: Legacy Bot
|
||||
enabled: true
|
||||
bus:
|
||||
nats_url: "nats://127.0.0.1:4250"
|
||||
ctrl_url: "http://127.0.0.1:8470"
|
||||
llm:
|
||||
primary:
|
||||
provider: openai
|
||||
model: gpt-4o
|
||||
|
||||
# These sections were removed from the schema but may still exist in old YAMLs.
|
||||
agents:
|
||||
peers:
|
||||
- id: other-bot
|
||||
capabilities: [general]
|
||||
room: "!abc:server.com"
|
||||
delegation:
|
||||
enabled: false
|
||||
protocol:
|
||||
format: json
|
||||
channel: matrix
|
||||
|
||||
observability:
|
||||
logging:
|
||||
level: info
|
||||
format: json
|
||||
metrics:
|
||||
enabled: false
|
||||
health:
|
||||
enabled: true
|
||||
port: 8080
|
||||
tracing:
|
||||
enabled: false
|
||||
|
||||
resilience:
|
||||
circuit_breaker:
|
||||
failure_threshold: 5
|
||||
timeout: 30s
|
||||
retry:
|
||||
max_attempts: 2
|
||||
backoff: exponential
|
||||
shutdown:
|
||||
timeout: 10s
|
||||
queue:
|
||||
enabled: true
|
||||
max_size: 100
|
||||
`
|
||||
var cfg AgentConfig
|
||||
if err := yaml.Unmarshal([]byte(yamlWithRemoved), &cfg); err != nil {
|
||||
t.Fatalf("parsing config with removed sections should succeed, got: %v", err)
|
||||
}
|
||||
if cfg.Agent.ID != "legacy-bot" {
|
||||
t.Errorf("expected agent.id=legacy-bot, got %q", cfg.Agent.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAgentConfigParseFull verifies that a config YAML with all active sections
|
||||
// parses correctly, including personality with communication.
|
||||
func TestAgentConfigParseFull(t *testing.T) {
|
||||
const fullYAML = `
|
||||
agent:
|
||||
id: full-bot
|
||||
name: Full Bot
|
||||
version: "1.0.0"
|
||||
enabled: true
|
||||
description: "A fully configured bot"
|
||||
tags: [test, full]
|
||||
|
||||
personality:
|
||||
tone: friendly
|
||||
verbosity: concise
|
||||
language: es
|
||||
role: "asistente general"
|
||||
communication:
|
||||
formality: semiformal
|
||||
humor: subtle
|
||||
personality: pragmatic
|
||||
response_style: structured
|
||||
quirks: ["usa analogias"]
|
||||
avoid_topics: ["politica"]
|
||||
catchphrases: ["interesante"]
|
||||
|
||||
llm:
|
||||
primary:
|
||||
provider: openai
|
||||
model: gpt-4o
|
||||
api_key_env: OPENAI_API_KEY
|
||||
max_tokens: 4096
|
||||
temperature: 0.7
|
||||
tool_use:
|
||||
enabled: true
|
||||
max_iterations: 5
|
||||
|
||||
bus:
|
||||
nats_url: "nats://127.0.0.1:4250"
|
||||
ctrl_url: "http://127.0.0.1:8470"
|
||||
handle: full
|
||||
threads:
|
||||
enabled: true
|
||||
auto_thread: false
|
||||
|
||||
tools:
|
||||
ssh:
|
||||
enabled: false
|
||||
http:
|
||||
enabled: true
|
||||
allowed_domains: ["api.example.com"]
|
||||
timeout: 10s
|
||||
|
||||
security:
|
||||
sanitize:
|
||||
enabled: true
|
||||
mode: warn
|
||||
min_severity: medium
|
||||
tool_rate_limit:
|
||||
enabled: true
|
||||
max_calls_per_min: 10
|
||||
|
||||
storage:
|
||||
base_path: "/data/full-bot"
|
||||
|
||||
memory:
|
||||
enabled: true
|
||||
window_size: 30
|
||||
|
||||
skills:
|
||||
enabled: true
|
||||
path: "skills/"
|
||||
categories: ["devops"]
|
||||
timeout: 60s
|
||||
`
|
||||
var cfg AgentConfig
|
||||
if err := yaml.Unmarshal([]byte(fullYAML), &cfg); err != nil {
|
||||
t.Fatalf("failed to parse full config: %v", err)
|
||||
}
|
||||
|
||||
// Verify key fields
|
||||
if cfg.Agent.ID != "full-bot" {
|
||||
t.Errorf("agent.id: got %q", cfg.Agent.ID)
|
||||
}
|
||||
if cfg.Personality.Communication.Humor != "subtle" {
|
||||
t.Errorf("personality.communication.humor: got %q", cfg.Personality.Communication.Humor)
|
||||
}
|
||||
if len(cfg.Personality.Communication.Quirks) != 1 {
|
||||
t.Errorf("personality.communication.quirks: expected 1, got %d", len(cfg.Personality.Communication.Quirks))
|
||||
}
|
||||
if !cfg.LLM.ToolUse.Enabled {
|
||||
t.Error("llm.tool_use.enabled should be true")
|
||||
}
|
||||
if !cfg.Tools.HTTP.Enabled {
|
||||
t.Error("tools.http.enabled should be true")
|
||||
}
|
||||
if cfg.Storage.BasePath != "/data/full-bot" {
|
||||
t.Errorf("storage.base_path: got %q", cfg.Storage.BasePath)
|
||||
}
|
||||
if !cfg.Memory.Enabled {
|
||||
t.Error("memory.enabled should be true")
|
||||
}
|
||||
if !cfg.Skills.Enabled {
|
||||
t.Error("skills.enabled should be true")
|
||||
}
|
||||
if !cfg.Security.Sanitize.Enabled {
|
||||
t.Error("security.sanitize.enabled should be true")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user