fix(tool-feedback): format tool args as JSON code blocks

This commit is contained in:
afjcjsbx
2026-04-24 18:07:48 +02:00
parent 8d51d306b3
commit 94a6b0c0f5
6 changed files with 87 additions and 17 deletions
+17 -7
View File
@@ -7,21 +7,31 @@ import (
const ToolFeedbackContinuationHint = "Continuing the current task."
// FormatToolFeedbackMessage renders the model-provided explanation for why a
// tool is being executed. When the model does not provide one, it keeps only
// the tool line and does not expose raw arguments or fallback text.
func FormatToolFeedbackMessage(toolName, explanation string) string {
// FormatToolFeedbackMessage renders a tool feedback message for chat channels.
// It keeps the tool name on the first line for animation and can include both
// a human explanation and the serialized tool arguments in the body.
func FormatToolFeedbackMessage(toolName, explanation, argsPreview string) string {
toolName = strings.TrimSpace(toolName)
explanation = strings.TrimSpace(explanation)
argsPreview = strings.TrimSpace(argsPreview)
bodyLines := make([]string, 0, 2)
if explanation != "" {
bodyLines = append(bodyLines, explanation)
}
if argsPreview != "" {
bodyLines = append(bodyLines, "```json\n"+argsPreview+"\n```")
}
body := strings.Join(bodyLines, "\n")
if toolName == "" {
return explanation
return body
}
if explanation == "" {
if body == "" {
return fmt.Sprintf("\U0001f527 `%s`", toolName)
}
return fmt.Sprintf("\U0001f527 `%s`\n%s", toolName, explanation)
return fmt.Sprintf("\U0001f527 `%s`\n%s", toolName, body)
}
// FitToolFeedbackMessage keeps tool feedback within a single outbound message.
+14 -5
View File
@@ -6,29 +6,38 @@ func TestFormatToolFeedbackMessage(t *testing.T) {
got := FormatToolFeedbackMessage(
"read_file",
"I will read README.md first to confirm the current project structure.",
"{\n \"path\": \"README.md\"\n}",
)
want := "\U0001f527 `read_file`\nI will read README.md first to confirm the current project structure."
want := "\U0001f527 `read_file`\nI will read README.md first to confirm the current project structure.\n```json\n{\n \"path\": \"README.md\"\n}\n```"
if got != want {
t.Fatalf("FormatToolFeedbackMessage() = %q, want %q", got, want)
}
}
func TestFormatToolFeedbackMessage_EmptyExplanationKeepsOnlyToolLine(t *testing.T) {
got := FormatToolFeedbackMessage("read_file", "")
want := "\U0001f527 `read_file`"
func TestFormatToolFeedbackMessage_EmptyExplanationShowsArgs(t *testing.T) {
got := FormatToolFeedbackMessage("read_file", "", "{\n \"path\": \"README.md\"\n}")
want := "\U0001f527 `read_file`\n```json\n{\n \"path\": \"README.md\"\n}\n```"
if got != want {
t.Fatalf("FormatToolFeedbackMessage() = %q, want %q", got, want)
}
}
func TestFormatToolFeedbackMessage_EmptyToolNameOmitsToolLine(t *testing.T) {
got := FormatToolFeedbackMessage("", "Continue drafting the final response.")
got := FormatToolFeedbackMessage("", "Continue drafting the final response.", "")
want := "Continue drafting the final response."
if got != want {
t.Fatalf("FormatToolFeedbackMessage() = %q, want %q", got, want)
}
}
func TestFormatToolFeedbackMessage_EmptyExplanationAndArgsKeepsOnlyToolLine(t *testing.T) {
got := FormatToolFeedbackMessage("read_file", "", "")
want := "\U0001f527 `read_file`"
if got != want {
t.Fatalf("FormatToolFeedbackMessage() = %q, want %q", got, want)
}
}
func TestFitToolFeedbackMessage_TruncatesBodyWithinSingleMessage(t *testing.T) {
got := FitToolFeedbackMessage(
"\U0001f527 `read_file`\nRead README.md first to confirm the current project structure.",