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"
|
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
|
// 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.
|
// 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"
|
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
|
return entry.Name
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package tools
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -218,7 +219,8 @@ func (t *CronTool) listJobs() *ToolResult {
|
||||||
return SilentResult("No scheduled jobs")
|
return SilentResult("No scheduled jobs")
|
||||||
}
|
}
|
||||||
|
|
||||||
result := "Scheduled jobs:\n"
|
var sb strings.Builder
|
||||||
|
sb.WriteString("Scheduled jobs:\n")
|
||||||
for _, j := range jobs {
|
for _, j := range jobs {
|
||||||
var scheduleInfo string
|
var scheduleInfo string
|
||||||
if j.Schedule.Kind == "every" && j.Schedule.EveryMS != nil {
|
if j.Schedule.Kind == "every" && j.Schedule.EveryMS != nil {
|
||||||
|
|
@ -230,10 +232,10 @@ func (t *CronTool) listJobs() *ToolResult {
|
||||||
} else {
|
} else {
|
||||||
scheduleInfo = "unknown"
|
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 {
|
func (t *CronTool) removeJob(args map[string]any) *ToolResult {
|
||||||
|
|
|
||||||
|
|
@ -239,10 +239,12 @@ func formatDirEntries(entries []os.DirEntry) *ToolResult {
|
||||||
var result strings.Builder
|
var result strings.Builder
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
result.WriteString("DIR: " + entry.Name() + "\n")
|
result.WriteString("DIR: ")
|
||||||
} else {
|
} else {
|
||||||
result.WriteString("FILE: " + entry.Name() + "\n")
|
result.WriteString("FILE: ")
|
||||||
}
|
}
|
||||||
|
result.WriteString(entry.Name())
|
||||||
|
result.WriteByte('\n')
|
||||||
}
|
}
|
||||||
return NewToolResult(result.String())
|
return NewToolResult(result.String())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -283,8 +283,8 @@ func (t *ExecTool) Execute(ctx context.Context, args map[string]any) *ToolResult
|
||||||
cwd = override
|
cwd = override
|
||||||
}
|
}
|
||||||
if wd, ok := args["working_dir"].(string); ok && wd != "" {
|
if wd, ok := args["working_dir"].(string); ok && wd != "" {
|
||||||
if t.restrictToWorkspace && t.workingDir != "" {
|
if t.restrictToWorkspace && cwd != "" {
|
||||||
resolvedWD, err := validatePath(wd, t.workingDir, true)
|
resolvedWD, err := validatePath(wd, cwd, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrorResult("Command blocked by safety guard (" + err.Error() + ")")
|
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 {
|
if stderr.Len() > 0 {
|
||||||
output += "\nSTDERR:\n" + stderr.String()
|
ob.WriteString("\nSTDERR:\n")
|
||||||
|
ob.WriteString(stderr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -378,8 +380,9 @@ func (t *ExecTool) executeSync(ctx context.Context, command, cwd string) *ToolRe
|
||||||
IsError: true,
|
IsError: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output += fmt.Sprintf("\nExit code: %v", err)
|
fmt.Fprintf(&ob, "\nExit code: %v", err)
|
||||||
}
|
}
|
||||||
|
output := ob.String()
|
||||||
|
|
||||||
if output == "" {
|
if output == "" {
|
||||||
output = "(no output)"
|
output = "(no output)"
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type workspaceOverrideKey struct{}
|
type workspaceOverrideKey struct{}
|
||||||
|
type overrideFsKey struct{}
|
||||||
|
|
||||||
// WithWorkspaceOverride returns a context carrying a workspace override path.
|
// WithWorkspaceOverride returns a context carrying a workspace override path
|
||||||
// Tools will resolve file operations against this path instead of the original workspace.
|
// 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 {
|
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 "".
|
// 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.
|
// resolveFS returns a fileSystem applying workspace override from context.
|
||||||
// Paths under "memory/" are excluded (always use original workspace).
|
// 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.
|
// For hostFs (unrestricted): returns as-is.
|
||||||
func resolveFS(ctx context.Context, fs fileSystem, path string) fileSystem {
|
func resolveFS(ctx context.Context, fs fileSystem, path string) fileSystem {
|
||||||
override := WorkspaceOverrideFromCtx(ctx)
|
override := WorkspaceOverrideFromCtx(ctx)
|
||||||
|
|
@ -42,6 +48,10 @@ func resolveFS(ctx context.Context, fs fileSystem, path string) fileSystem {
|
||||||
if sfs.workspace == override {
|
if sfs.workspace == override {
|
||||||
return fs
|
return fs
|
||||||
}
|
}
|
||||||
|
// Use cached sandboxFs from context
|
||||||
|
if cached, ok := ctx.Value(overrideFsKey{}).(*sandboxFs); ok {
|
||||||
|
return cached
|
||||||
|
}
|
||||||
return &sandboxFs{workspace: override}
|
return &sandboxFs{workspace: override}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue