Core features: - Microkernel architecture with Actor model - Session management with JSONL persistence - Tool system (5 built-in tools) - Skill system with SKILL.md parsing - Sandbox security execution - Ollama integration with gemma4:e4b - Prompt-based tool calling (compatible with native function calling) - REPL interface 11 packages, all tests passing
109 lines
2.4 KiB
Go
109 lines
2.4 KiB
Go
package tool
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"sync"
|
|
)
|
|
|
|
// Manager is a thread-safe registry that manages tool registration and execution.
|
|
//
|
|
// Tools are registered by name (case-sensitive) and can be discovered,
|
|
// listed, and invoked through the Manager. Duplicate registration returns
|
|
// an error.
|
|
type Manager struct {
|
|
mu sync.RWMutex
|
|
tools map[string]Tool
|
|
}
|
|
|
|
// NewManager creates a new empty tool manager.
|
|
func NewManager() *Manager {
|
|
return &Manager{
|
|
tools: make(map[string]Tool),
|
|
}
|
|
}
|
|
|
|
// Register adds a tool to the manager. Returns an error if a tool with the
|
|
// same name is already registered.
|
|
func (m *Manager) Register(tool Tool) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
name := tool.Name()
|
|
if _, exists := m.tools[name]; exists {
|
|
return fmt.Errorf("tool %q is already registered", name)
|
|
}
|
|
|
|
m.tools[name] = tool
|
|
return nil
|
|
}
|
|
|
|
// Unregister removes a tool from the manager by name.
|
|
func (m *Manager) Unregister(name string) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if _, exists := m.tools[name]; !exists {
|
|
return fmt.Errorf("tool %q is not registered", name)
|
|
}
|
|
|
|
delete(m.tools, name)
|
|
return nil
|
|
}
|
|
|
|
// Get retrieves a tool by name. Returns false if not found.
|
|
func (m *Manager) Get(name string) (Tool, bool) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
t, ok := m.tools[name]
|
|
return t, ok
|
|
}
|
|
|
|
// List returns all registered tools sorted by name.
|
|
func (m *Manager) List() []Tool {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
result := make([]Tool, 0, len(m.tools))
|
|
for _, t := range m.tools {
|
|
result = append(result, t)
|
|
}
|
|
|
|
sort.Slice(result, func(i, j int) bool {
|
|
return result[i].Name() < result[j].Name()
|
|
})
|
|
return result
|
|
}
|
|
|
|
// Execute looks up a tool by name and invokes it with the given arguments.
|
|
// Returns an error if the tool is not found.
|
|
func (m *Manager) Execute(name string, ctx context.Context, args map[string]interface{}) (*Result, error) {
|
|
tool, ok := m.Get(name)
|
|
if !ok {
|
|
return nil, fmt.Errorf("tool %q not found", name)
|
|
}
|
|
return tool.Execute(ctx, args)
|
|
}
|
|
|
|
// Count returns the number of registered tools.
|
|
func (m *Manager) Count() int {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
return len(m.tools)
|
|
}
|
|
|
|
// Names returns the names of all registered tools sorted alphabetically.
|
|
func (m *Manager) Names() []string {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
names := make([]string, 0, len(m.tools))
|
|
for name := range m.tools {
|
|
names = append(names, name)
|
|
}
|
|
sort.Strings(names)
|
|
return names
|
|
}
|