perf: reduce []byte/string conversions and add ASCII fast paths
- bytes.NewReader instead of strings.NewReader(string(...)) in web search - Consolidate multiple string(body) calls into single variable - sb.Write(paramsJSON) instead of string conversion in CLI provider - ASCII fast paths in Truncate/wrapLine before []rune conversion - Byte-length truncation for ASCII-only branch names - Extract runeDisplayWidth to avoid per-rune string(r) in wrap loop Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
66c5f2d897
commit
77e0862827
5 changed files with 45 additions and 31 deletions
|
|
@ -1036,28 +1036,32 @@ func formatMarkdownTable(lines []string) string {
|
|||
return strings.TrimRight(b.String(), "\n")
|
||||
}
|
||||
|
||||
func runeDisplayWidth(r rune) int {
|
||||
switch {
|
||||
case r == '\u200d' || r == '\u200c' || r == '\ufe0f':
|
||||
return 0
|
||||
case unicode.Is(unicode.Mn, r):
|
||||
return 0
|
||||
case isEmojiRune(r):
|
||||
return 3
|
||||
case unicode.In(r,
|
||||
unicode.Han,
|
||||
unicode.Hiragana,
|
||||
unicode.Katakana,
|
||||
unicode.Hangul):
|
||||
return 2
|
||||
case (r >= 0x3000 && r <= 0x303F) || (r >= 0xFF00 && r <= 0xFFEF):
|
||||
// CJK symbols/punctuation and half/fullwidth forms.
|
||||
return 2
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func displayWidth(s string) int {
|
||||
w := 0
|
||||
for _, r := range s {
|
||||
switch {
|
||||
case r == '\u200d' || r == '\u200c' || r == '\ufe0f':
|
||||
continue
|
||||
case unicode.Is(unicode.Mn, r):
|
||||
continue
|
||||
case isEmojiRune(r):
|
||||
w += 3
|
||||
case unicode.In(r,
|
||||
unicode.Han,
|
||||
unicode.Hiragana,
|
||||
unicode.Katakana,
|
||||
unicode.Hangul):
|
||||
w += 2
|
||||
case (r >= 0x3000 && r <= 0x303F) || (r >= 0xFF00 && r <= 0xFFEF):
|
||||
// CJK symbols/punctuation and half/fullwidth forms.
|
||||
w += 2
|
||||
default:
|
||||
w++
|
||||
}
|
||||
w += runeDisplayWidth(r)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
|
@ -1092,7 +1096,7 @@ func wrapByDisplayWidth(s string, maxWidth int) []string {
|
|||
flush()
|
||||
continue
|
||||
}
|
||||
rw := displayWidth(string(r))
|
||||
rw := runeDisplayWidth(r)
|
||||
if rw == 0 {
|
||||
cur.WriteRune(r)
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -67,12 +67,11 @@ func SanitizeBranchName(task string) string {
|
|||
s = "worktree"
|
||||
}
|
||||
|
||||
// Truncate to 40 chars
|
||||
runes := []rune(s)
|
||||
if len(runes) > 40 {
|
||||
runes = runes[:40]
|
||||
// Truncate to 40 chars (ASCII fast path: branch names are ASCII after sanitization)
|
||||
if len(s) > 40 {
|
||||
s = s[:40]
|
||||
}
|
||||
s = strings.TrimRight(string(runes), "-")
|
||||
s = strings.TrimRight(s, "-")
|
||||
|
||||
return "plan/" + s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,9 @@ func (p *ClaudeCliProvider) buildToolsPrompt(tools []ToolDefinition) string {
|
|||
}
|
||||
if len(tool.Function.Parameters) > 0 {
|
||||
paramsJSON, _ := json.Marshal(tool.Function.Parameters)
|
||||
sb.WriteString(fmt.Sprintf("Parameters:\n```json\n%s\n```\n", string(paramsJSON)))
|
||||
sb.WriteString("Parameters:\n```json\n")
|
||||
sb.Write(paramsJSON)
|
||||
sb.WriteString("\n```\n")
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ func (p *PerplexitySearchProvider) Search(ctx context.Context, query string, cou
|
|||
return "", fmt.Errorf("failed to marshal request: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", searchURL, strings.NewReader(string(payloadBytes)))
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", searchURL, bytes.NewReader(payloadBytes))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
|
@ -534,6 +534,7 @@ func (t *WebFetchTool) Execute(ctx context.Context, args map[string]any) *ToolRe
|
|||
|
||||
var text, extractor string
|
||||
|
||||
bodyStr := string(body)
|
||||
if strings.Contains(contentType, "application/json") {
|
||||
var jsonData any
|
||||
if err := json.Unmarshal(body, &jsonData); err == nil {
|
||||
|
|
@ -541,15 +542,15 @@ func (t *WebFetchTool) Execute(ctx context.Context, args map[string]any) *ToolRe
|
|||
text = string(formatted)
|
||||
extractor = "json"
|
||||
} else {
|
||||
text = string(body)
|
||||
text = bodyStr
|
||||
extractor = "raw"
|
||||
}
|
||||
} else if strings.Contains(contentType, "text/html") || len(body) > 0 &&
|
||||
(strings.HasPrefix(string(body), "<!DOCTYPE") || strings.HasPrefix(strings.ToLower(string(body)), "<html")) {
|
||||
text = t.extractText(string(body))
|
||||
(strings.HasPrefix(bodyStr, "<!DOCTYPE") || strings.HasPrefix(strings.ToLower(bodyStr), "<html")) {
|
||||
text = t.extractText(bodyStr)
|
||||
extractor = "text"
|
||||
} else {
|
||||
text = string(body)
|
||||
text = bodyStr
|
||||
extractor = "raw"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ func TailPad(s string, n, wrapWidth int) string {
|
|||
// wrapLine splits a single line into segments of at most width runes.
|
||||
// An empty line produces one empty string (preserving blank lines).
|
||||
func wrapLine(line string, width int) []string {
|
||||
// ASCII fast path: byte length == rune length for pure ASCII
|
||||
if len(line) <= width {
|
||||
return []string{line}
|
||||
}
|
||||
runes := []rune(line)
|
||||
if len(runes) <= width {
|
||||
return []string{line}
|
||||
|
|
@ -97,6 +101,10 @@ func Truncate(s string, maxLen int) string {
|
|||
if maxLen <= 0 {
|
||||
return ""
|
||||
}
|
||||
// ASCII fast path: byte length == rune length for pure ASCII
|
||||
if len(s) <= maxLen {
|
||||
return s
|
||||
}
|
||||
runes := []rune(s)
|
||||
if len(runes) <= maxLen {
|
||||
return s
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue