perf: replace string concatenation with strings.Builder in hot paths
- loop.go: formatCompactEntry/formatLatestEntry (called 4-5x per iteration) - filesystem.go: formatDirEntries loop (per directory entry) - shell.go: executeSync output assembly, fix validatePath to use override-aware cwd instead of original workspace - cron.go: listJobs loop - workspace_ctx.go: cache override sandboxFs in context to reuse across all resolveFS calls within the same tool execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4a6fc48ebd
commit
3e0245bd3d
5 changed files with 51 additions and 17 deletions
|
|
@ -1271,11 +1271,23 @@ func formatCompactEntry(entry toolLogEntry) string {
|
|||
args = string(argsRunes[:argsBudget-1]) + "\u2026"
|
||||
}
|
||||
}
|
||||
return entry.Name + " " + args + " " + result
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(entry.Name) + 1 + len(args) + 1 + len(result))
|
||||
sb.WriteString(entry.Name)
|
||||
sb.WriteByte(' ')
|
||||
sb.WriteString(args)
|
||||
sb.WriteByte(' ')
|
||||
sb.WriteString(result)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// No room for args or args empty
|
||||
return entry.Name + " " + result
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(entry.Name) + 1 + len(result))
|
||||
sb.WriteString(entry.Name)
|
||||
sb.WriteByte(' ')
|
||||
sb.WriteString(result)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// formatLatestEntry formats the latest entry command without its result marker.
|
||||
|
|
@ -1294,7 +1306,12 @@ func formatLatestEntry(entry toolLogEntry) string {
|
|||
args = string(argsRunes[:argsBudget-1]) + "\u2026"
|
||||
}
|
||||
}
|
||||
return entry.Name + " " + args
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(entry.Name) + 1 + len(args))
|
||||
sb.WriteString(entry.Name)
|
||||
sb.WriteByte(' ')
|
||||
sb.WriteString(args)
|
||||
return sb.String()
|
||||
}
|
||||
return entry.Name
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package tools
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
|
@ -218,7 +219,8 @@ func (t *CronTool) listJobs() *ToolResult {
|
|||
return SilentResult("No scheduled jobs")
|
||||
}
|
||||
|
||||
result := "Scheduled jobs:\n"
|
||||
var sb strings.Builder
|
||||
sb.WriteString("Scheduled jobs:\n")
|
||||
for _, j := range jobs {
|
||||
var scheduleInfo string
|
||||
if j.Schedule.Kind == "every" && j.Schedule.EveryMS != nil {
|
||||
|
|
@ -230,10 +232,10 @@ func (t *CronTool) listJobs() *ToolResult {
|
|||
} else {
|
||||
scheduleInfo = "unknown"
|
||||
}
|
||||
result += fmt.Sprintf("- %s (id: %s, %s)\n", j.Name, j.ID, scheduleInfo)
|
||||
fmt.Fprintf(&sb, "- %s (id: %s, %s)\n", j.Name, j.ID, scheduleInfo)
|
||||
}
|
||||
|
||||
return SilentResult(result)
|
||||
return SilentResult(sb.String())
|
||||
}
|
||||
|
||||
func (t *CronTool) removeJob(args map[string]any) *ToolResult {
|
||||
|
|
|
|||
|
|
@ -239,10 +239,12 @@ func formatDirEntries(entries []os.DirEntry) *ToolResult {
|
|||
var result strings.Builder
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
result.WriteString("DIR: " + entry.Name() + "\n")
|
||||
result.WriteString("DIR: ")
|
||||
} else {
|
||||
result.WriteString("FILE: " + entry.Name() + "\n")
|
||||
result.WriteString("FILE: ")
|
||||
}
|
||||
result.WriteString(entry.Name())
|
||||
result.WriteByte('\n')
|
||||
}
|
||||
return NewToolResult(result.String())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -283,8 +283,8 @@ func (t *ExecTool) Execute(ctx context.Context, args map[string]any) *ToolResult
|
|||
cwd = override
|
||||
}
|
||||
if wd, ok := args["working_dir"].(string); ok && wd != "" {
|
||||
if t.restrictToWorkspace && t.workingDir != "" {
|
||||
resolvedWD, err := validatePath(wd, t.workingDir, true)
|
||||
if t.restrictToWorkspace && cwd != "" {
|
||||
resolvedWD, err := validatePath(wd, cwd, true)
|
||||
if err != nil {
|
||||
return ErrorResult("Command blocked by safety guard (" + err.Error() + ")")
|
||||
}
|
||||
|
|
@ -364,9 +364,11 @@ func (t *ExecTool) executeSync(ctx context.Context, command, cwd string) *ToolRe
|
|||
}
|
||||
}
|
||||
|
||||
output := stdout.String()
|
||||
var ob strings.Builder
|
||||
ob.WriteString(stdout.String())
|
||||
if stderr.Len() > 0 {
|
||||
output += "\nSTDERR:\n" + stderr.String()
|
||||
ob.WriteString("\nSTDERR:\n")
|
||||
ob.WriteString(stderr.String())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -378,8 +380,9 @@ func (t *ExecTool) executeSync(ctx context.Context, command, cwd string) *ToolRe
|
|||
IsError: true,
|
||||
}
|
||||
}
|
||||
output += fmt.Sprintf("\nExit code: %v", err)
|
||||
fmt.Fprintf(&ob, "\nExit code: %v", err)
|
||||
}
|
||||
output := ob.String()
|
||||
|
||||
if output == "" {
|
||||
output = "(no output)"
|
||||
|
|
|
|||
|
|
@ -7,11 +7,17 @@ import (
|
|||
)
|
||||
|
||||
type workspaceOverrideKey struct{}
|
||||
type overrideFsKey struct{}
|
||||
|
||||
// WithWorkspaceOverride returns a context carrying a workspace override path.
|
||||
// Tools will resolve file operations against this path instead of the original workspace.
|
||||
// WithWorkspaceOverride returns a context carrying a workspace override path
|
||||
// and a pre-built sandboxFs for that workspace. Tools will resolve file
|
||||
// operations against this path instead of the original workspace.
|
||||
// The cached sandboxFs is reused across all resolveFS calls on the same context,
|
||||
// avoiding per-operation allocation.
|
||||
func WithWorkspaceOverride(ctx context.Context, workspace string) context.Context {
|
||||
return context.WithValue(ctx, workspaceOverrideKey{}, workspace)
|
||||
ctx = context.WithValue(ctx, workspaceOverrideKey{}, workspace)
|
||||
ctx = context.WithValue(ctx, overrideFsKey{}, &sandboxFs{workspace: workspace})
|
||||
return ctx
|
||||
}
|
||||
|
||||
// WorkspaceOverrideFromCtx extracts the workspace override from context, or "".
|
||||
|
|
@ -24,7 +30,7 @@ func WorkspaceOverrideFromCtx(ctx context.Context) string {
|
|||
|
||||
// resolveFS returns a fileSystem applying workspace override from context.
|
||||
// Paths under "memory/" are excluded (always use original workspace).
|
||||
// For sandboxFs: creates a temporary instance with the override workspace.
|
||||
// For sandboxFs: returns the cached override instance from context.
|
||||
// For hostFs (unrestricted): returns as-is.
|
||||
func resolveFS(ctx context.Context, fs fileSystem, path string) fileSystem {
|
||||
override := WorkspaceOverrideFromCtx(ctx)
|
||||
|
|
@ -42,6 +48,10 @@ func resolveFS(ctx context.Context, fs fileSystem, path string) fileSystem {
|
|||
if sfs.workspace == override {
|
||||
return fs
|
||||
}
|
||||
// Use cached sandboxFs from context
|
||||
if cached, ok := ctx.Value(overrideFsKey{}).(*sandboxFs); ok {
|
||||
return cached
|
||||
}
|
||||
return &sandboxFs{workspace: override}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue