Optimize inferMediaType by avoiding strings.ToLower allocation

- Moves strings.ToLower to after the empty extension check
- Adds fast paths for well-formed content types to bypass ToLower entirely

Co-authored-by: hobbyistlabs-coder <267281733+hobbyistlabs-coder@users.noreply.github.com>
This commit is contained in:
google-labs-jules[bot] 2026-03-18 17:39:56 +00:00
parent 8b9a4fce62
commit cfc622d848
2 changed files with 33 additions and 14 deletions

View file

@ -8,3 +8,6 @@
## 2024-05-26 - Avoid Unnecessary `strings.ToLower` ## 2024-05-26 - Avoid Unnecessary `strings.ToLower`
**Learning:** Calling `strings.ToLower` on the entire message content allocates a new string and iterates over all runes. This causes measurable GC pressure and latency on hot paths like feature extraction during routing. **Learning:** Calling `strings.ToLower` on the entire message content allocates a new string and iterates over all runes. This causes measurable GC pressure and latency on hot paths like feature extraction during routing.
**Action:** Use fast paths to bypass `strings.ToLower`. For instance, check if a requisite character (like a dot `.`) exists, or check common casings directly (`DATA:IMAGE` vs `data:image`) before falling back to full case-normalization. **Action:** Use fast paths to bypass `strings.ToLower`. For instance, check if a requisite character (like a dot `.`) exists, or check common casings directly (`DATA:IMAGE` vs `data:image`) before falling back to full case-normalization.
## 2025-03-20 - String Operations Fast Paths and Avoiding Double Searches
**Learning:** When trying to optimize `strings.ToLower`, ensure you don't introduce regressions with byte-to-rune casting on UTF-8 strings. Also, `strings.Contains(s, sub)` literally calls `strings.Index(s, sub)` under the hood. Using `strings.Contains` followed immediately by `strings.Index` to extract the position is an anti-pattern that searches the string twice, undermining the intended performance optimization.
**Action:** Always prefer a single `strings.Index` call over `Contains`+`Index`. Stick to one single optimization per PR to reduce risk and review burden.

View file

@ -102,28 +102,44 @@ func formatToolsForLog(toolDefs []providers.ToolDefinition) string {
// inferMediaType determines the media type ("image", "audio", "video", "file") // inferMediaType determines the media type ("image", "audio", "video", "file")
// from a filename and MIME content type. // from a filename and MIME content type.
func inferMediaType(filename, contentType string) string { func inferMediaType(filename, contentType string) string {
ct := strings.ToLower(contentType) // Fast path for common, correctly cased content types
fn := strings.ToLower(filename) if strings.HasPrefix(contentType, "image/") {
if strings.HasPrefix(ct, "image/") {
return "image" return "image"
} }
if strings.HasPrefix(ct, "audio/") || ct == "application/ogg" { if strings.HasPrefix(contentType, "audio/") || contentType == "application/ogg" {
return "audio" return "audio"
} }
if strings.HasPrefix(ct, "video/") { if strings.HasPrefix(contentType, "video/") {
return "video" return "video"
} }
// Slower path for uppercase or mixed case
if contentType != "" {
ct := strings.ToLower(contentType)
if strings.HasPrefix(ct, "image/") {
return "image"
}
if strings.HasPrefix(ct, "audio/") || ct == "application/ogg" {
return "audio"
}
if strings.HasPrefix(ct, "video/") {
return "video"
}
}
// Fallback: infer from extension // Fallback: infer from extension
ext := filepath.Ext(fn) ext := filepath.Ext(filename)
switch ext { if ext != "" {
case ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg": // Only lower case the extension, which is usually just a few characters
return "image" ext = strings.ToLower(ext)
case ".mp3", ".wav", ".ogg", ".m4a", ".flac", ".aac", ".wma", ".opus": switch ext {
return "audio" case ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg":
case ".mp4", ".avi", ".mov", ".webm", ".mkv": return "image"
return "video" case ".mp3", ".wav", ".ogg", ".m4a", ".flac", ".aac", ".wma", ".opus":
return "audio"
case ".mp4", ".avi", ".mov", ".webm", ".mkv":
return "video"
}
} }
return "file" return "file"