feat(agent): add pretty_print and disable_escape_html options for tool feedback

- Add PrettyPrint and DisableEscapeHTML config options to ToolFeedbackConfig
- Add FormatArgsJSON helper function with configurable pretty printing and HTML escaping
- Add toolFeedbackArgsPreviewWithOptions to pass formatting options
- Update pipeline_execute.go to use new formatting options for tool feedback

This fixes the issue where '&&' would be displayed as '\u0026' in tool
feedback messages and provides optional pretty-printing for better
readability.
This commit is contained in:
David Siewert 2026-04-25 20:46:16 +06:00
parent 77be169db4
commit bcc3d447a1
5 changed files with 38 additions and 8 deletions

View file

@ -184,6 +184,15 @@ func toolFeedbackArgsPreview(args map[string]any, maxLen int) string {
return utils.Truncate(string(argsJSON), maxLen) return utils.Truncate(string(argsJSON), maxLen)
} }
func toolFeedbackArgsPreviewWithOptions(args map[string]any, maxLen int, prettyPrint, disableEscapeHTML bool) string {
if args == nil {
args = map[string]any{}
}
argsJSON := utils.FormatArgsJSON(args, prettyPrint, disableEscapeHTML)
return utils.Truncate(argsJSON, maxLen)
}
func shouldPublishToolFeedback(cfg *config.Config, ts *turnState) bool { func shouldPublishToolFeedback(cfg *config.Config, ts *turnState) bool {
if ts == nil || ts.channel == "" || ts.opts.SuppressToolFeedback { if ts == nil || ts.channel == "" || ts.opts.SuppressToolFeedback {
return false return false

View file

@ -91,7 +91,7 @@ toolLoop:
feedbackMsg := utils.FormatToolFeedbackMessage( feedbackMsg := utils.FormatToolFeedbackMessage(
toolName, toolName,
toolFeedbackExplanation, toolFeedbackExplanation,
toolFeedbackArgsPreview(toolArgs, toolFeedbackMaxLen), toolFeedbackArgsPreviewWithOptions(toolArgs, toolFeedbackMaxLen, al.cfg.Agents.Defaults.ToolFeedback.PrettyPrint, al.cfg.Agents.Defaults.ToolFeedback.DisableEscapeHTML),
) )
fbCtx, fbCancel := context.WithTimeout(turnCtx, 3*time.Second) fbCtx, fbCancel := context.WithTimeout(turnCtx, 3*time.Second)
_ = al.bus.PublishOutbound(fbCtx, outboundMessageForTurnWithKind(ts, feedbackMsg, messageKindToolFeedback)) _ = al.bus.PublishOutbound(fbCtx, outboundMessageForTurnWithKind(ts, feedbackMsg, messageKindToolFeedback))
@ -373,7 +373,7 @@ toolLoop:
feedbackMsg := utils.FormatToolFeedbackMessage( feedbackMsg := utils.FormatToolFeedbackMessage(
toolName, toolName,
toolFeedbackExplanation, toolFeedbackExplanation,
toolFeedbackArgsPreview(toolArgs, toolFeedbackMaxLen), toolFeedbackArgsPreviewWithOptions(toolArgs, toolFeedbackMaxLen, al.cfg.Agents.Defaults.ToolFeedback.PrettyPrint, al.cfg.Agents.Defaults.ToolFeedback.DisableEscapeHTML),
) )
fbCtx, fbCancel := context.WithTimeout(turnCtx, 3*time.Second) fbCtx, fbCancel := context.WithTimeout(turnCtx, 3*time.Second)
_ = al.bus.PublishOutbound(fbCtx, outboundMessageForTurnWithKind(ts, feedbackMsg, messageKindToolFeedback)) _ = al.bus.PublishOutbound(fbCtx, outboundMessageForTurnWithKind(ts, feedbackMsg, messageKindToolFeedback))

View file

@ -250,6 +250,8 @@ type ToolFeedbackConfig struct {
Enabled bool `json:"enabled" env:"PICOCLAW_AGENTS_DEFAULTS_TOOL_FEEDBACK_ENABLED"` Enabled bool `json:"enabled" env:"PICOCLAW_AGENTS_DEFAULTS_TOOL_FEEDBACK_ENABLED"`
MaxArgsLength int `json:"max_args_length" env:"PICOCLAW_AGENTS_DEFAULTS_TOOL_FEEDBACK_MAX_ARGS_LENGTH"` MaxArgsLength int `json:"max_args_length" env:"PICOCLAW_AGENTS_DEFAULTS_TOOL_FEEDBACK_MAX_ARGS_LENGTH"`
SeparateMessages bool `json:"separate_messages" env:"PICOCLAW_AGENTS_DEFAULTS_TOOL_FEEDBACK_SEPARATE_MESSAGES"` SeparateMessages bool `json:"separate_messages" env:"PICOCLAW_AGENTS_DEFAULTS_TOOL_FEEDBACK_SEPARATE_MESSAGES"`
PrettyPrint bool `json:"pretty_print" env:"PICOCLAW_AGENTS_DEFAULTS_TOOL_FEEDBACK_PRETTY_PRINT"`
DisableEscapeHTML bool `json:"disable_escape_html" env:"PICOCLAW_AGENTS_DEFAULTS_TOOL_FEEDBACK_DISABLE_ESCAPE_HTML"`
} }
type AgentDefaults struct { type AgentDefaults struct {

View file

@ -38,6 +38,8 @@ func DefaultConfig() *Config {
Enabled: false, Enabled: false,
MaxArgsLength: 300, MaxArgsLength: 300,
SeparateMessages: false, SeparateMessages: false,
PrettyPrint: true,
DisableEscapeHTML: true,
}, },
SplitOnMarker: false, SplitOnMarker: false,
}, },

View file

@ -1,12 +1,29 @@
package utils package utils
import ( import (
"bytes"
"encoding/json"
"fmt" "fmt"
"strings" "strings"
) )
const ToolFeedbackContinuationHint = "Continuing the current task." const ToolFeedbackContinuationHint = "Continuing the current task."
func FormatArgsJSON(args map[string]any, prettyPrint, disableEscapeHTML bool) string {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
if prettyPrint {
enc.SetIndent("", " ")
}
if disableEscapeHTML {
enc.SetEscapeHTML(false)
}
if err := enc.Encode(args); err != nil {
return "{}"
}
return strings.TrimSpace(buf.String())
}
// FormatToolFeedbackMessage renders a tool feedback message for chat channels. // FormatToolFeedbackMessage renders a tool feedback message for chat channels.
// It keeps the tool name on the first line for animation and can include both // 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. // a human explanation and the serialized tool arguments in the body.