42251ecada
Divide agents/runtime.go (1188 lineas) en 5 archivos especializados: - runtime.go (350 lineas): struct Agent, New(), Run(), Stop(), lifecycle - handler.go: handleEvent(), executeActions(), command routing, bus, sanitizacion - llm.go: runLLM(), tool-use loop, system prompt, initLLM(), prompt-commands - memory.go: ensureWindowLoaded(), appendToWindow(), persistMessage(), ClearWindow() - registry_build.go: buildToolRegistry(), initToolDeps(), initRateLimiter() Zero cambios en API publica. Todos los metodos siguen siendo del struct Agent, solo viven en archivos separados por responsabilidad. Funciones helper extraidas de New() para reducir su tamaño: - initCrypto(): inicializacion E2EE - initLLM(): cliente LLM con fallback - initMemoryStore(): store SQLite + window size - initToolDeps(): knowledge, MCP, skills - initRateLimiter(): rate limiting de tools Reduccion: 1188 → 350 lineas en runtime.go (70% menos).
120 lines
3.4 KiB
Go
120 lines
3.4 KiB
Go
package agents
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"path/filepath"
|
|
|
|
coretypes "github.com/enmanuel/agents/pkg/llm"
|
|
"github.com/enmanuel/agents/pkg/memory"
|
|
shellmem "github.com/enmanuel/agents/shell/memory"
|
|
)
|
|
|
|
// ClearWindow resets the conversation window for a room and deletes persisted
|
|
// messages from SQLite so the agent starts fresh. Implements toolmemory.WindowClearer.
|
|
func (a *Agent) ClearWindow(roomID string) {
|
|
a.windowsMu.Lock()
|
|
a.windows[roomID] = memory.NewWindow(a.windowSize)
|
|
a.windowsMu.Unlock()
|
|
|
|
if a.memStore != nil {
|
|
if err := a.memStore.DeleteMessages(
|
|
context.Background(), a.cfg.Agent.ID, &roomID,
|
|
); err != nil {
|
|
a.logger.Warn("failed to delete persisted messages on clear", "room", roomID, "err", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ensureWindowLoaded loads the conversation window from SQLite on first access for a room.
|
|
func (a *Agent) ensureWindowLoaded(ctx context.Context, roomID string) {
|
|
a.windowsMu.Lock()
|
|
defer a.windowsMu.Unlock()
|
|
if _, ok := a.windows[roomID]; ok {
|
|
return
|
|
}
|
|
w := memory.NewWindow(a.windowSize)
|
|
if a.memStore != nil {
|
|
msgs, err := a.memStore.LoadMessages(ctx, a.cfg.Agent.ID, roomID, a.windowSize)
|
|
if err != nil {
|
|
a.logger.Warn("failed to load message history", "room", roomID, "err", err)
|
|
} else {
|
|
for _, m := range msgs {
|
|
w = w.Append(coretypes.Message{Role: m.Role, Content: m.Content})
|
|
}
|
|
if len(msgs) > 0 {
|
|
a.logger.Debug("loaded message history", "room", roomID, "count", len(msgs))
|
|
}
|
|
}
|
|
}
|
|
a.windows[roomID] = w
|
|
}
|
|
|
|
// appendToWindow adds a message to the in-memory conversation window.
|
|
func (a *Agent) appendToWindow(roomID string, msg coretypes.Message) {
|
|
a.windowsMu.Lock()
|
|
defer a.windowsMu.Unlock()
|
|
w, ok := a.windows[roomID]
|
|
if !ok {
|
|
w = memory.NewWindow(a.windowSize)
|
|
}
|
|
a.windows[roomID] = w.Append(msg)
|
|
}
|
|
|
|
// getWindowMessages returns a copy of the conversation window for a room.
|
|
func (a *Agent) getWindowMessages(roomID string) []coretypes.Message {
|
|
a.windowsMu.RLock()
|
|
defer a.windowsMu.RUnlock()
|
|
w, ok := a.windows[roomID]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return w.ToLLMMessages()
|
|
}
|
|
|
|
// persistMessage saves a message to the SQLite store (no-op if store is nil).
|
|
func (a *Agent) persistMessage(ctx context.Context, roomID string, role coretypes.Role, content string) {
|
|
if a.memStore == nil {
|
|
return
|
|
}
|
|
if err := a.memStore.SaveMessage(ctx, memory.HistoryMessage{
|
|
AgentID: a.cfg.Agent.ID,
|
|
RoomID: roomID,
|
|
Role: role,
|
|
Content: content,
|
|
}); err != nil {
|
|
a.logger.Warn("failed to persist message", "room", roomID, "err", err)
|
|
}
|
|
}
|
|
|
|
// memoryInit holds the results of memory subsystem initialization.
|
|
type memoryInit struct {
|
|
store memory.Store
|
|
windowSize int
|
|
}
|
|
|
|
// initMemoryStore creates the memory store and resolves window size from config.
|
|
// Returns a zero-value memoryInit if memory is disabled.
|
|
func initMemoryStore(enabled bool, windowSizeCfg int, dbPathCfg string, dataBase string, logger *slog.Logger) (memoryInit, error) {
|
|
if !enabled {
|
|
return memoryInit{windowSize: defaultWindowSize}, nil
|
|
}
|
|
|
|
windowSize := windowSizeCfg
|
|
if windowSize <= 0 {
|
|
windowSize = defaultWindowSize
|
|
}
|
|
|
|
dbPath := dbPathCfg
|
|
if dbPath == "" {
|
|
dbPath = filepath.Join(dataBase, "memory.db")
|
|
}
|
|
store, err := shellmem.New(dbPath, logger)
|
|
if err != nil {
|
|
return memoryInit{}, fmt.Errorf("memory store: %w", err)
|
|
}
|
|
logger.Info("memory enabled", "window_size", windowSize, "db", dbPath)
|
|
return memoryInit{store: store, windowSize: windowSize}, nil
|
|
}
|