feat: integrar structured logging en todos los componentes del shell
Se propaga *slog.Logger a todos los componentes impuros del shell: - shell/bus/ — logs de subscribe, send, reply, timeout, unsubscribe - shell/effects/ — duración y resultado de cada action ejecutada - shell/llm/ (anthropic, openai, factory) — request/response con tokens, duración, fallback - shell/memory/sqlite — open, save, recall, close con detalles - shell/ssh/ — inicio, fin, errores y duración de comandos SSH - tools/registry — registro, ejecución y errores de herramientas Se usa el paquete shell/logger para field names consistentes (FieldDurationMS, FieldTokensUsed, etc.). Cada componente recibe el logger por inyección de dependencias, sin globals. Las firmas de New/FromConfig se actualizan para aceptar *slog.Logger. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+25
-4
@@ -4,24 +4,32 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
coretypes "github.com/enmanuel/agents/pkg/llm"
|
||||
"github.com/enmanuel/agents/shell/logger"
|
||||
)
|
||||
|
||||
// Registry holds available tools keyed by name.
|
||||
type Registry struct {
|
||||
tools map[string]Tool
|
||||
tools map[string]Tool
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// NewRegistry creates an empty registry.
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{tools: make(map[string]Tool)}
|
||||
func NewRegistry(log *slog.Logger) *Registry {
|
||||
return &Registry{
|
||||
tools: make(map[string]Tool),
|
||||
logger: log.With(logger.FieldComponent, "tools"),
|
||||
}
|
||||
}
|
||||
|
||||
// Register adds a tool to the registry.
|
||||
func (r *Registry) Register(t Tool) {
|
||||
r.tools[t.Def.Name] = t
|
||||
r.logger.Debug("tool_registered", "name", t.Def.Name)
|
||||
}
|
||||
|
||||
// Get looks up a tool by name.
|
||||
@@ -49,17 +57,30 @@ func (r *Registry) Len() int {
|
||||
func (r *Registry) Execute(ctx context.Context, name string, argsJSON string) Result {
|
||||
t, ok := r.tools[name]
|
||||
if !ok {
|
||||
r.logger.Warn("tool_not_found", "tool", name)
|
||||
return Result{Err: fmt.Errorf("tool %q not found", name)}
|
||||
}
|
||||
|
||||
var args map[string]any
|
||||
if argsJSON != "" {
|
||||
if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
|
||||
r.logger.Warn("tool_args_invalid", "tool", name, "err", err)
|
||||
return Result{Err: fmt.Errorf("parse args for %q: %w", name, err)}
|
||||
}
|
||||
}
|
||||
|
||||
return t.Exec(ctx, args)
|
||||
r.logger.Info("tool_exec_start", "tool", name)
|
||||
start := time.Now()
|
||||
result := t.Exec(ctx, args)
|
||||
ms := time.Since(start).Milliseconds()
|
||||
|
||||
if result.Err != nil {
|
||||
r.logger.Warn("tool_exec_error", "tool", name, "err", result.Err, logger.FieldDurationMS, ms)
|
||||
} else {
|
||||
r.logger.Info("tool_exec_end", "tool", name, logger.FieldDurationMS, ms)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ToLLMSpecs converts all registered tools to the LLM-compatible ToolSpec format.
|
||||
|
||||
Reference in New Issue
Block a user