From fcbe9666bddf20c6c996b04891a962d374687226 Mon Sep 17 00:00:00 2001 From: Kujtim Hoxha Date: Thu, 31 Jul 2025 10:49:07 +0200 Subject: [PATCH 1/3] chore: limit tool output --- internal/llm/tools/tools.go | 75 ++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/internal/llm/tools/tools.go b/internal/llm/tools/tools.go index 41c0515616032b117f3c09a0056cac9e86b62c66..1759c3d33ef4e685be82c868d82e914c501174aa 100644 --- a/internal/llm/tools/tools.go +++ b/internal/llm/tools/tools.go @@ -3,6 +3,8 @@ package tools import ( "context" "encoding/json" + "fmt" + "strings" ) type ToolInfo struct { @@ -25,6 +27,10 @@ const ( SessionIDContextKey sessionIDContextKey = "session_id" MessageIDContextKey messageIDContextKey = "message_id" + + MaxResponseWidth = 3000 + MaxResponseHeight = 5000 + MaxResponseChars = 50000 ) type ToolResponse struct { @@ -37,10 +43,77 @@ type ToolResponse struct { func NewTextResponse(content string) ToolResponse { return ToolResponse{ Type: ToolResponseTypeText, - Content: content, + Content: truncateContent(content), } } +func truncateContent(content string) string { + if len(content) <= MaxResponseChars { + return truncateWidthAndHeight(content) + } + + truncated := content[:MaxResponseChars] + + if lastNewline := strings.LastIndex(truncated, "\n"); lastNewline > MaxResponseChars/2 { + truncated = truncated[:lastNewline] + } + + truncated += "\n\n... [Content truncated due to length] ..." + + return truncateWidthAndHeight(truncated) +} + +func truncateWidthAndHeight(content string) string { + lines := strings.Split(content, "\n") + + heightTruncated := false + if len(lines) > MaxResponseHeight { + keepLines := MaxResponseHeight - 3 + firstHalf := keepLines / 2 + secondHalf := keepLines - firstHalf + + truncatedLines := make([]string, 0, MaxResponseHeight) + truncatedLines = append(truncatedLines, lines[:firstHalf]...) + truncatedLines = append(truncatedLines, "") + truncatedLines = append(truncatedLines, fmt.Sprintf("... [%d lines truncated] ...", len(lines)-keepLines)) + truncatedLines = append(truncatedLines, "") + truncatedLines = append(truncatedLines, lines[len(lines)-secondHalf:]...) + + lines = truncatedLines + heightTruncated = true + } + + widthTruncated := false + for i, line := range lines { + if len(line) > MaxResponseWidth { + if MaxResponseWidth > 20 { + keepChars := MaxResponseWidth - 10 + firstHalf := keepChars / 2 + secondHalf := keepChars - firstHalf + lines[i] = line[:firstHalf] + " ... " + line[len(line)-secondHalf:] + } else { + lines[i] = line[:MaxResponseWidth] + } + widthTruncated = true + } + } + + result := strings.Join(lines, "\n") + + if heightTruncated || widthTruncated { + notices := []string{} + if heightTruncated { + notices = append(notices, "height") + } + if widthTruncated { + notices = append(notices, "width") + } + result += fmt.Sprintf("\n\n[Note: Content truncated by %s to fit response limits]", strings.Join(notices, " and ")) + } + + return result +} + func WithResponseMetadata(response ToolResponse, metadata any) ToolResponse { if metadata != nil { metadataBytes, err := json.Marshal(metadata) From 7bfd0469acc8bb5cdbed13787f7c04fcaa12f660 Mon Sep 17 00:00:00 2001 From: Kujtim Hoxha Date: Fri, 1 Aug 2025 08:06:25 +0200 Subject: [PATCH 2/3] chore: make consts private --- internal/llm/tools/tools.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/internal/llm/tools/tools.go b/internal/llm/tools/tools.go index 1759c3d33ef4e685be82c868d82e914c501174aa..641794cd74fd662c645c0f4cd634961c28cf915c 100644 --- a/internal/llm/tools/tools.go +++ b/internal/llm/tools/tools.go @@ -28,9 +28,9 @@ const ( SessionIDContextKey sessionIDContextKey = "session_id" MessageIDContextKey messageIDContextKey = "message_id" - MaxResponseWidth = 3000 - MaxResponseHeight = 5000 - MaxResponseChars = 50000 + maxResponseWidth = 3000 + maxResponseHeight = 5000 + maxResponseChars = 50000 ) type ToolResponse struct { @@ -48,13 +48,13 @@ func NewTextResponse(content string) ToolResponse { } func truncateContent(content string) string { - if len(content) <= MaxResponseChars { + if len(content) <= maxResponseChars { return truncateWidthAndHeight(content) } - truncated := content[:MaxResponseChars] + truncated := content[:maxResponseChars] - if lastNewline := strings.LastIndex(truncated, "\n"); lastNewline > MaxResponseChars/2 { + if lastNewline := strings.LastIndex(truncated, "\n"); lastNewline > maxResponseChars/2 { truncated = truncated[:lastNewline] } @@ -67,12 +67,12 @@ func truncateWidthAndHeight(content string) string { lines := strings.Split(content, "\n") heightTruncated := false - if len(lines) > MaxResponseHeight { - keepLines := MaxResponseHeight - 3 + if len(lines) > maxResponseHeight { + keepLines := maxResponseHeight - 3 firstHalf := keepLines / 2 secondHalf := keepLines - firstHalf - truncatedLines := make([]string, 0, MaxResponseHeight) + truncatedLines := make([]string, 0, maxResponseHeight) truncatedLines = append(truncatedLines, lines[:firstHalf]...) truncatedLines = append(truncatedLines, "") truncatedLines = append(truncatedLines, fmt.Sprintf("... [%d lines truncated] ...", len(lines)-keepLines)) @@ -85,14 +85,14 @@ func truncateWidthAndHeight(content string) string { widthTruncated := false for i, line := range lines { - if len(line) > MaxResponseWidth { - if MaxResponseWidth > 20 { - keepChars := MaxResponseWidth - 10 + if len(line) > maxResponseWidth { + if maxResponseWidth > 20 { + keepChars := maxResponseWidth - 10 firstHalf := keepChars / 2 secondHalf := keepChars - firstHalf lines[i] = line[:firstHalf] + " ... " + line[len(line)-secondHalf:] } else { - lines[i] = line[:MaxResponseWidth] + lines[i] = line[:maxResponseWidth] } widthTruncated = true } From e130e0385682a0ede0a63630e527328a4d187e49 Mon Sep 17 00:00:00 2001 From: Kujtim Hoxha Date: Mon, 4 Aug 2025 10:38:11 +0200 Subject: [PATCH 3/3] chore: set notices size --- internal/llm/tools/tools.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/llm/tools/tools.go b/internal/llm/tools/tools.go index 641794cd74fd662c645c0f4cd634961c28cf915c..d8eb9b30c10378c06700d82a584eab19294f99ae 100644 --- a/internal/llm/tools/tools.go +++ b/internal/llm/tools/tools.go @@ -101,7 +101,7 @@ func truncateWidthAndHeight(content string) string { result := strings.Join(lines, "\n") if heightTruncated || widthTruncated { - notices := []string{} + notices := make([]string, 0, 2) if heightTruncated { notices = append(notices, "height") }