agent/toolsrv: replace classifier system with ToolMeta map
The entire classifier indirection (interfaces, type assertions, RPC methods for classification, closure wrappers in BuildRuntime) is replaced by a simple map[string]ToolInfo on Runtime. The .meta file is read once into ToolInfo. The loop reads fields directly: rt.ToolMeta[name].ReadOnly (was: rt.CanParallelize(name)) rt.ToolMeta[name].Tier (was: rt.MemoryTier(name, args)) rt.ToolMeta[name].OutputFormat (was: rt.OutputFormat(name)) rt.ToolMeta[name].ResetsCounter (was: rt.ResetsCounter(name)) Removed: ParallelClassifier, MemoryTierClassifier, OutputFormatClassifier interfaces, toolClassifier type, and all closure-wrapping in BuildRuntime. The RPC methods on Server/Conn stay (used by ollie-remote) but the agent no longer routes through them.
This commit is contained in:
parent
38b35f1ba5
commit
b6694d409a
|
|
@ -67,51 +67,11 @@ func BuildRuntime(cfg *AgentConfig, srv toolsrv.Runner, cwd string, env []string
|
||||||
return text, blocks, nil
|
return text, blocks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var classify toolClassifier
|
// Build ToolMeta lookup from the tool listing — single source of truth
|
||||||
if pc, ok := srv.(toolsrv.ParallelClassifier); ok {
|
// for all per-tool metadata (tier, readOnly, outputFormat, resetsCounter).
|
||||||
classify = pc.CanParallelize
|
toolMeta := make(map[string]toolsrv.ToolInfo, len(allToolInfos))
|
||||||
}
|
|
||||||
|
|
||||||
// Capture output formats from the same tool listing used to build the
|
|
||||||
// backend tool definitions. Remote runners expose ToolInfo through
|
|
||||||
// ListTools, but do not necessarily implement OutputFormatClassifier.
|
|
||||||
outputFormats := make(map[string]string, len(allToolInfos))
|
|
||||||
for _, ti := range allToolInfos {
|
for _, ti := range allToolInfos {
|
||||||
outputFormats[ti.Name] = ti.OutputFormat
|
toolMeta[ti.Name] = ti
|
||||||
}
|
|
||||||
outputFormat := func(name string) string {
|
|
||||||
return outputFormats[name]
|
|
||||||
}
|
|
||||||
if oc, ok := srv.(toolsrv.OutputFormatClassifier); ok {
|
|
||||||
outputFormat = oc.OutputFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
var tierFn func(string, json.RawMessage) ResultTier
|
|
||||||
if tc, ok := srv.(toolsrv.MemoryTierClassifier); ok {
|
|
||||||
tierFn = func(name string, args json.RawMessage) ResultTier {
|
|
||||||
switch tc.MemoryTierArgs(name, args) {
|
|
||||||
case "cold":
|
|
||||||
return TierCold
|
|
||||||
case "warm":
|
|
||||||
return TierWarm
|
|
||||||
default:
|
|
||||||
return TierHot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build resetsCounter lookup from tool infos.
|
|
||||||
resetsCounterMap := make(map[string]bool, len(allToolInfos))
|
|
||||||
for _, ti := range allToolInfos {
|
|
||||||
if ti.ResetsCounter {
|
|
||||||
resetsCounterMap[ti.Name] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var resetsCounterFn func(string) bool
|
|
||||||
if len(resetsCounterMap) > 0 {
|
|
||||||
resetsCounterFn = func(name string) bool {
|
|
||||||
return resetsCounterMap[name]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var compactionModel string
|
var compactionModel string
|
||||||
|
|
@ -124,11 +84,8 @@ func BuildRuntime(cfg *AgentConfig, srv toolsrv.Runner, cwd string, env []string
|
||||||
return &Runtime{
|
return &Runtime{
|
||||||
ToolServer: srv,
|
ToolServer: srv,
|
||||||
Tools: allTools,
|
Tools: allTools,
|
||||||
|
ToolMeta: toolMeta,
|
||||||
Exec: exec,
|
Exec: exec,
|
||||||
CanParallelize: classify,
|
|
||||||
OutputFormat: outputFormat,
|
|
||||||
MemoryTier: tierFn,
|
|
||||||
ResetsCounter: resetsCounterFn,
|
|
||||||
Preamble: preamble,
|
Preamble: preamble,
|
||||||
GenParams: genParams,
|
GenParams: genParams,
|
||||||
MaxSteps: maxSteps,
|
MaxSteps: maxSteps,
|
||||||
|
|
|
||||||
|
|
@ -99,10 +99,22 @@ func fileStat(path string) (int64, int64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func toolOutputFormat(rt *Runtime, name string) string {
|
func toolOutputFormat(rt *Runtime, name string) string {
|
||||||
if rt.OutputFormat == nil {
|
if ti, ok := rt.ToolMeta[name]; ok {
|
||||||
return ""
|
return ti.OutputFormat
|
||||||
}
|
}
|
||||||
return rt.OutputFormat(name)
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func toolMemoryTier(rt *Runtime, name string) ResultTier {
|
||||||
|
if ti, ok := rt.ToolMeta[name]; ok {
|
||||||
|
switch ti.Tier {
|
||||||
|
case "cold":
|
||||||
|
return TierCold
|
||||||
|
case "warm":
|
||||||
|
return TierWarm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TierHot
|
||||||
}
|
}
|
||||||
|
|
||||||
// TurnCtx holds per-turn closures and state that vary between turns within
|
// TurnCtx holds per-turn closures and state that vary between turns within
|
||||||
|
|
@ -317,7 +329,7 @@ func run(rt *Runtime, ctx TurnCtx, state state) error {
|
||||||
return cr, true
|
return cr, true
|
||||||
}
|
}
|
||||||
emit(ctx, Event{Role: "call", Name: call.Name, Content: string(call.Arguments)})
|
emit(ctx, Event{Role: "call", Name: call.Name, Content: string(call.Arguments)})
|
||||||
readSafe := rt.CanParallelize != nil && rt.CanParallelize(call.Name)
|
readSafe := rt.ToolMeta[call.Name].ReadOnly
|
||||||
if readSafe {
|
if readSafe {
|
||||||
key := call.Name + "\x00" + string(call.Arguments)
|
key := call.Name + "\x00" + string(call.Arguments)
|
||||||
if v, ok := resultCache.Load(key); ok {
|
if v, ok := resultCache.Load(key); ok {
|
||||||
|
|
@ -420,14 +432,14 @@ func run(rt *Runtime, ctx TurnCtx, state state) error {
|
||||||
emit(ctx, Event{Role: "tool", Name: call.Name, OutputFormat: toolOutputFormat(rt, call.Name)})
|
emit(ctx, Event{Role: "tool", Name: call.Name, OutputFormat: toolOutputFormat(rt, call.Name)})
|
||||||
}
|
}
|
||||||
tier := TierHot
|
tier := TierHot
|
||||||
if !isErr && rt.MemoryTier != nil {
|
if !isErr {
|
||||||
tier = rt.MemoryTier(call.Name, call.Arguments)
|
tier = toolMemoryTier(rt, call.Name)
|
||||||
}
|
}
|
||||||
return toolResult{ToolCallID: call.ID, Name: call.Name, Content: result, ContentBlocks: resultBlocks, IsError: isErr, Tier: tier}, false
|
return toolResult{ToolCallID: call.ID, Name: call.Name, Content: result, ContentBlocks: resultBlocks, IsError: isErr, Tier: tier}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
isParallelSafe := func(name string) bool {
|
isParallelSafe := func(name string) bool {
|
||||||
return name != "" && rt.CanParallelize != nil && rt.CanParallelize(name)
|
return name != "" && rt.ToolMeta[name].ReadOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(toolCalls) && !interrupted; {
|
for i := 0; i < len(toolCalls) && !interrupted; {
|
||||||
|
|
@ -644,9 +656,9 @@ func run(rt *Runtime, ctx TurnCtx, state state) error {
|
||||||
// This allows the agent to continue working without hitting the soft
|
// This allows the agent to continue working without hitting the soft
|
||||||
// step-budget guardrail, as long as it's making active progress (writing
|
// step-budget guardrail, as long as it's making active progress (writing
|
||||||
// files, renaming symbols, etc.) rather than looping on research.
|
// files, renaming symbols, etc.) rather than looping on research.
|
||||||
if rt.ResetsCounter != nil {
|
if rt.ToolMeta != nil {
|
||||||
for _, r := range results {
|
for _, r := range results {
|
||||||
if !r.IsError && rt.ResetsCounter(r.Name) {
|
if !r.IsError && rt.ToolMeta[r.Name].ResetsCounter {
|
||||||
step = 0
|
step = 0
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -1079,6 +1091,4 @@ func retryCountdown(ctx TurnCtx, wait time.Duration) error {
|
||||||
// defaultToolResultMaxBytes caps tool result content sent back to the model.
|
// defaultToolResultMaxBytes caps tool result content sent back to the model.
|
||||||
const defaultToolResultMaxBytes = 131072
|
const defaultToolResultMaxBytes = 131072
|
||||||
|
|
||||||
// toolClassifier reports whether a named tool is safe to run concurrently
|
|
||||||
// with other read-class toolsrv. nil means treat all tools as serial.
|
|
||||||
type toolClassifier func(name string) bool
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package agent
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"ollie/backend"
|
"ollie/backend"
|
||||||
"ollie/toolsrv"
|
"ollie/toolsrv"
|
||||||
)
|
)
|
||||||
|
|
@ -12,17 +10,14 @@ import (
|
||||||
// agent. The agent struct stores a pointer to the active Runtime; switching
|
// agent. The agent struct stores a pointer to the active Runtime; switching
|
||||||
// agents replaces it atomically.
|
// agents replaces it atomically.
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
Backend backend.Backend
|
Backend backend.Backend
|
||||||
ToolServer toolsrv.Runner // the execute server (tool runtime, env, cwd)
|
ToolServer toolsrv.Runner // the execute server (tool runtime, env, cwd)
|
||||||
Preamble string // compiled system prompt
|
Preamble string // compiled system prompt
|
||||||
Tools []backend.Tool
|
Tools []backend.Tool
|
||||||
Exec toolExecutor
|
ToolMeta map[string]toolsrv.ToolInfo // keyed by tool name — source of truth for all per-tool metadata
|
||||||
CanParallelize toolClassifier
|
Exec toolExecutor
|
||||||
OutputFormat func(string) string
|
GenParams backend.GenerationParams
|
||||||
MemoryTier func(string, json.RawMessage) ResultTier
|
MaxSteps int
|
||||||
ResetsCounter func(string) bool // true if tool resets step counter
|
|
||||||
GenParams backend.GenerationParams
|
|
||||||
MaxSteps int
|
|
||||||
// toolRegistryRevision tracks the last-seen tool registry revision. When the
|
// toolRegistryRevision tracks the last-seen tool registry revision. When the
|
||||||
// registry revision advances (after tool_load/unload), the next turn
|
// registry revision advances (after tool_load/unload), the next turn
|
||||||
// will detect the mismatch and refetch.
|
// will detect the mismatch and refetch.
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"ollie/backend"
|
"ollie/backend"
|
||||||
|
"ollie/toolsrv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Submit processes one line of user input: it starts an agent turn that streams
|
// Submit processes one line of user input: it starts an agent turn that streams
|
||||||
|
|
@ -111,13 +112,11 @@ func (ag *Agent) executeTurn(ctx context.Context, input string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
ag.runtime.Tools = toolInfosToBackend(ti)
|
ag.runtime.Tools = toolInfosToBackend(ti)
|
||||||
outputFormats := make(map[string]string, len(ti))
|
meta := make(map[string]toolsrv.ToolInfo, len(ti))
|
||||||
for _, info := range ti {
|
for _, info := range ti {
|
||||||
outputFormats[info.Name] = info.OutputFormat
|
meta[info.Name] = info
|
||||||
}
|
|
||||||
ag.runtime.OutputFormat = func(name string) string {
|
|
||||||
return outputFormats[name]
|
|
||||||
}
|
}
|
||||||
|
ag.runtime.ToolMeta = meta
|
||||||
}
|
}
|
||||||
|
|
||||||
turnCtx := TurnCtx{
|
turnCtx := TurnCtx{
|
||||||
|
|
|
||||||
|
|
@ -36,25 +36,3 @@ type Runner interface {
|
||||||
ListTools() ([]ToolInfo, error)
|
ListTools() ([]ToolInfo, error)
|
||||||
CallTool(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error)
|
CallTool(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParallelClassifier is implemented by tool servers that can report whether a
|
|
||||||
// named tool is safe to run concurrently with other read-class tools.
|
|
||||||
// Returns false for unknown tools (conservative default).
|
|
||||||
type ParallelClassifier interface {
|
|
||||||
CanParallelize(name string) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// MemoryTierClassifier is implemented by tool servers that can report the retention
|
|
||||||
// tier of a tool's results: "hot" (verbatim), "warm" (summarized on compaction),
|
|
||||||
// or "cold" (immediately summarized). Returns "hot" for unknown tools.
|
|
||||||
type MemoryTierClassifier interface {
|
|
||||||
MemoryTier(name string) string
|
|
||||||
// MemoryTierArgs classifies the tier using both the outer tool name and its
|
|
||||||
// arguments.
|
|
||||||
MemoryTierArgs(name string, args json.RawMessage) string
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputFormatClassifier reports the source-fence language for tool output.
|
|
||||||
type OutputFormatClassifier interface {
|
|
||||||
OutputFormat(name string) string
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue