agent: eliminate Dispatcher, use tools.Server directly

Runtime.Dispatcher → Runtime.ExecServer (tools.Server). The Dispatcher
abstraction routed to exactly one server ('execute') — unnecessary
indirection. BuildRuntime now takes tools.Server directly.

Agent.newDispatcher → Agent.newToolServer. AgentCfg/Config updated.
All GetServer('execute') calls replaced with direct ExecServer access.
exec closure simplified: direct CallTool, no tool-name lookup.
This commit is contained in:
Levi Neely 2026-07-29 22:50:01 +02:00
parent 0524f38db0
commit d6bc89d3e2
6 changed files with 35 additions and 59 deletions

View File

@ -29,7 +29,7 @@ type Agent struct {
agentsDir string agentsDir string
baseLayers []string // system prompt layers for /agent reloads baseLayers []string // system prompt layers for /agent reloads
promptEnvExtra []string // PRIME_* vars for prompt resolution promptEnvExtra []string // PRIME_* vars for prompt resolution
newDispatcher func() tools.Dispatcher newToolServer func() tools.Server
newBackend func(string) (backend.Backend, error) newBackend func(string) (backend.Backend, error)
currentAction atomic.Pointer[actionHandle] currentAction atomic.Pointer[actionHandle]
warnedContext bool warnedContext bool
@ -258,10 +258,10 @@ func (ag *Agent) ToolCallCount() int64 {
// SetSessionEnv injects session env vars into the execute server. // SetSessionEnv injects session env vars into the execute server.
func (ag *Agent) SetSessionEnv(sessionID string) { func (ag *Agent) SetSessionEnv(sessionID string) {
if ag.runtime.Dispatcher == nil { if ag.runtime.ExecServer == nil {
return return
} }
if srv, ok := ag.runtime.Dispatcher.GetServer("execute"); ok { if srv := ag.runtime.ExecServer; srv != nil {
if es, ok := srv.(tools.EnvSetter); ok { if es, ok := srv.(tools.EnvSetter); ok {
es.SetEnv("OLLIE_SESSION_ID", sessionID) es.SetEnv("OLLIE_SESSION_ID", sessionID)
if ag.id != "" { if ag.id != "" {
@ -273,10 +273,10 @@ func (ag *Agent) SetSessionEnv(sessionID string) {
// SetEnv stores an environment variable on the execute server. // SetEnv stores an environment variable on the execute server.
func (ag *Agent) SetEnv(key, value string) { func (ag *Agent) SetEnv(key, value string) {
if ag.runtime.Dispatcher == nil { if ag.runtime.ExecServer == nil {
return return
} }
if srv, ok := ag.runtime.Dispatcher.GetServer("execute"); ok { if srv := ag.runtime.ExecServer; srv != nil {
if es, ok := srv.(tools.EnvSetter); ok { if es, ok := srv.(tools.EnvSetter); ok {
es.SetEnv(key, value) es.SetEnv(key, value)
} }
@ -285,10 +285,10 @@ func (ag *Agent) SetEnv(key, value string) {
// Close releases agent resources (dispatcher, execute server). // Close releases agent resources (dispatcher, execute server).
func (ag *Agent) Close() { func (ag *Agent) Close() {
if ag.runtime.Dispatcher == nil { if ag.runtime.ExecServer == nil {
return return
} }
if srv, ok := ag.runtime.Dispatcher.GetServer("execute"); ok { if srv := ag.runtime.ExecServer; srv != nil {
if c, ok := srv.(interface{ Close() }); ok { if c, ok := srv.(interface{ Close() }); ok {
c.Close() c.Close()
} }
@ -302,8 +302,8 @@ func (ag *Agent) SetCWD(dir string) {
if oldCwd != "" && dir != "" && oldCwd != dir { if oldCwd != "" && dir != "" && oldCwd != dir {
ag.runtime.Preamble = strings.ReplaceAll(ag.runtime.Preamble, oldCwd, dir) ag.runtime.Preamble = strings.ReplaceAll(ag.runtime.Preamble, oldCwd, dir)
} }
if ag.runtime != nil && ag.runtime.Dispatcher != nil { if ag.runtime.ExecServer != nil {
if srv, ok := ag.runtime.Dispatcher.GetServer("execute"); ok { if srv := ag.runtime.ExecServer; srv != nil {
if ws, ok := srv.(tools.CWDSetter); ok { if ws, ok := srv.(tools.CWDSetter); ok {
ws.SetCWD(dir) ws.SetCWD(dir)
} }
@ -472,13 +472,9 @@ func (ag *Agent) React(responseID, emoji string) error {
return nil return nil
} }
// execServer returns the execute server interface, or nil if unavailable. // execServer returns the execute server, or nil if unavailable.
func (ag *Agent) execServer() interface{} { func (ag *Agent) execServer() tools.Server {
if ag.runtime.Dispatcher == nil { return ag.runtime.ExecServer
return nil
}
srv, _ := ag.runtime.Dispatcher.GetServer("execute")
return srv
} }
// Queue pushes a prompt onto the agent's FIFO. // Queue pushes a prompt onto the agent's FIFO.

View File

@ -17,7 +17,7 @@ import (
// env provides additional environment variables injected into prompt resolution // env provides additional environment variables injected into prompt resolution
// subprocesses (e.g. OLLIE_SESSION_ID=xxx). // subprocesses (e.g. OLLIE_SESSION_ID=xxx).
// The caller is responsible for registering all servers on d before calling this. // The caller is responsible for registering all servers on d before calling this.
func BuildRuntime(cfg *AgentConfig, d tools.Dispatcher, cwd string, env []string, baseLayers ...string) *Runtime { func BuildRuntime(cfg *AgentConfig, srv tools.Server, cwd string, env []string, baseLayers ...string) *Runtime {
var messages []string var messages []string
var allToolInfos []tools.ToolInfo var allToolInfos []tools.ToolInfo
@ -25,7 +25,7 @@ func BuildRuntime(cfg *AgentConfig, d tools.Dispatcher, cwd string, env []string
if cfg == nil || cfg.ToolsEnabled() { if cfg == nil || cfg.ToolsEnabled() {
var listErr error var listErr error
allToolInfos, listErr = d.ListTools() allToolInfos, listErr = srv.ListTools()
if listErr != nil { if listErr != nil {
messages = append(messages, fmt.Sprintf("list tools: %v", listErr)) messages = append(messages, fmt.Sprintf("list tools: %v", listErr))
} }
@ -69,30 +69,14 @@ func BuildRuntime(cfg *AgentConfig, d tools.Dispatcher, cwd string, env []string
} }
maxSteps = cfg.MaxSteps maxSteps = cfg.MaxSteps
if len(cfg.AllowTools) > 0 { if len(cfg.AllowTools) > 0 {
if srv, ok := d.GetServer("execute"); ok { if rs, ok := srv.(tools.ToolRestrictionSetter); ok {
if rs, ok := srv.(tools.ToolRestrictionSetter); ok { rs.SetAllowTools(cfg.AllowTools)
rs.SetAllowTools(cfg.AllowTools)
}
} }
} }
} }
exec := func(ctx context.Context, name string, args json.RawMessage) (string, []backend.ContentBlock, error) { exec := func(ctx context.Context, name string, args json.RawMessage) (string, []backend.ContentBlock, error) {
infos, listErr := d.ListTools() raw, err := srv.CallTool(ctx, name, args)
if listErr != nil {
return "", nil, listErr
}
server := ""
for _, t := range infos {
if t.Name == name {
server = t.Server
break
}
}
if server == "" {
return "", nil, fmt.Errorf("unknown tool: %s", name)
}
raw, err := d.Dispatch(ctx, server, name, args)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -104,24 +88,20 @@ func BuildRuntime(cfg *AgentConfig, d tools.Dispatcher, cwd string, env []string
} }
var classify toolClassifier var classify toolClassifier
if srv, ok := d.GetServer("execute"); ok { if pc, ok := srv.(tools.ParallelClassifier); ok {
if pc, ok := srv.(tools.ParallelClassifier); ok { classify = pc.IsParallelRead
classify = pc.IsParallelRead
}
} }
var tierFn func(string, json.RawMessage) ResultTier var tierFn func(string, json.RawMessage) ResultTier
if srv, ok := d.GetServer("execute"); ok { if tc, ok := srv.(tools.TierClassifier); ok {
if tc, ok := srv.(tools.TierClassifier); ok { tierFn = func(name string, args json.RawMessage) ResultTier {
tierFn = func(name string, args json.RawMessage) ResultTier { switch tc.ResultTierArgs(name, args) {
switch tc.ResultTierArgs(name, args) { case "cold":
case "cold": return TierCold
return TierCold case "warm":
case "warm": return TierWarm
return TierWarm default:
default: return TierHot
return TierHot
}
} }
} }
} }
@ -160,7 +140,7 @@ func BuildRuntime(cfg *AgentConfig, d tools.Dispatcher, cwd string, env []string
} }
return &Runtime{ return &Runtime{
Dispatcher: d, ExecServer: srv,
Tools: allTools, Tools: allTools,
Exec: exec, Exec: exec,
ClassifyTool: classify, ClassifyTool: classify,

View File

@ -180,7 +180,7 @@ func (ag *Agent) cmdAgent(_ context.Context, args []string) {
ag.emit(infoEvent(fmt.Sprintf("error: agent %q: %v", name, err))) ag.emit(infoEvent(fmt.Sprintf("error: agent %q: %v", name, err)))
return return
} }
disp := ag.newDispatcher() disp := ag.newToolServer()
env := []string{"OLLIE_SESSION_ID=" + ag.sessionID, "OLLIE_UNAME=" + ag.id} env := []string{"OLLIE_SESSION_ID=" + ag.sessionID, "OLLIE_UNAME=" + ag.id}
env = append(env, ag.promptEnvExtra...) env = append(env, ag.promptEnvExtra...)
rt := BuildRuntime(cfg, disp, ag.cwd, env, ag.baseLayers...) rt := BuildRuntime(cfg, disp, ag.cwd, env, ag.baseLayers...)

View File

@ -19,7 +19,7 @@ type AgentCfg struct {
CWD string // working directory for tool execution CWD string // working directory for tool execution
BaseLayers []string BaseLayers []string
PromptEnvExtra []string PromptEnvExtra []string
NewDispatcher func() tools.Dispatcher NewToolServer func() tools.Server
NewBackend func(string) (backend.Backend, error) NewBackend func(string) (backend.Backend, error)
Bus *pubsub.Bus Bus *pubsub.Bus
Log *olog.Logger Log *olog.Logger
@ -42,7 +42,7 @@ func NewAgent(cfg AgentCfg) *Agent {
cwd: cfg.CWD, cwd: cfg.CWD,
baseLayers: cfg.BaseLayers, baseLayers: cfg.BaseLayers,
promptEnvExtra: cfg.PromptEnvExtra, promptEnvExtra: cfg.PromptEnvExtra,
newDispatcher: cfg.NewDispatcher, newToolServer: cfg.NewToolServer,
newBackend: cfg.NewBackend, newBackend: cfg.NewBackend,
bus: cfg.Bus, bus: cfg.Bus,
log: cfg.Log, log: cfg.Log,

View File

@ -13,7 +13,7 @@ import (
// agents replaces it atomically. // agents replaces it atomically.
type Runtime struct { type Runtime struct {
Backend backend.Backend Backend backend.Backend
Dispatcher tools.Dispatcher ExecServer tools.Server // the execute server (tool runtime, env, cwd)
Hooks Hooks Hooks Hooks
Preamble string // compiled system prompt Preamble string // compiled system prompt
Tools []backend.Tool Tools []backend.Tool

View File

@ -32,7 +32,7 @@ type Config struct {
CWD string CWD string
History *agent.History History *agent.History
Runtime *agent.Runtime Runtime *agent.Runtime
NewDispatcher func() tools.Dispatcher NewToolServer func() tools.Server
NewBackend func(string) (backend.Backend, error) NewBackend func(string) (backend.Backend, error)
Log *olog.Logger Log *olog.Logger
MaxSteps int MaxSteps int
@ -150,7 +150,7 @@ func New(cfg Config) *Session {
CWD: paths.ExpandHome(cfg.CWD), CWD: paths.ExpandHome(cfg.CWD),
BaseLayers: cfg.BaseLayers, BaseLayers: cfg.BaseLayers,
PromptEnvExtra: cfg.PromptEnvExtra, PromptEnvExtra: cfg.PromptEnvExtra,
NewDispatcher: cfg.NewDispatcher, NewToolServer: cfg.NewToolServer,
NewBackend: cfg.NewBackend, NewBackend: cfg.NewBackend,
Bus: bus, Bus: bus,
Log: log, Log: log,