Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions lib/roast/cogs/agent/providers/pi/messages/tool_call_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,32 @@ def format_grep
details.any? ? "#{base} (#{details.join(", ")})" : base
end

# Formats a find tool call.
#
# Input fields:
# :pattern (String) – filename glob to match [required]
# :path (String) – directory to search in [optional]
# :limit (Integer) – max number of results [optional]
#
# Output: "FIND <pattern> (path: <path>, limit: <limit>)" – the "path:"
# and "limit:" details are each included only when present and joined
# with ", " inside a single trailing "(...)".
#
# Examples:
# FIND *.rb (path: lib, limit: 50)
# FIND *.rb (limit: 50)
# FIND *.rb
#
#: () -> String
def format_find
pattern, path, limit = arguments.values_at(:pattern, :path, :limit)
details = [
("path: #{path}" if path.present?),
("limit: #{limit}" if limit.present?),
].compact
details.any? ? "FIND #{pattern} (#{details.join(", ")})" : "FIND #{pattern}"
end

# Formats a tool call for which Roast has no dedicated formatter.
#
# Output: "<NAME> <key>: <value>, ..." – the upcased tool name, then each
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,32 @@ def format_grep
ok_line("#{count} #{"match".pluralize(count)}", note)
end

# Formats a find tool result.
#
# Content: matching paths, one per line, plus an optional status line –
# either a bracketed notice ("[2 results limit reached. ...]") or the
# no-results prose ("No files found matching pattern").
#
# Output: "FIND OK <n> <path|paths>[ · NOTE <status>]" – <n> counts the
# path lines only, and the notice's brackets are dropped. As in #format_grep,
# the NOTE is shown only alongside results: "0 paths" already says what the
# no-results prose would.
#
# Examples:
# FIND OK 12 paths
# FIND OK 1 path
# FIND OK 2 paths · NOTE 2 results limit reached. Use limit=4 for m...
# FIND OK 0 paths
#
#: () -> String
def format_find
Comment thread
LasmarKhalifa marked this conversation as resolved.
lines = content.to_s.lines.map(&:strip).reject(&:empty?)
notes, paths = lines.partition { |line| line.sub!(/\A\[(.*)\]\z/, '\1') || line.match?(/\ANo files found/) }
count = paths.length
note = "NOTE #{truncate(notes.join(" "))}" if paths.any? && notes.any?
ok_line("#{count} #{"path".pluralize(count)}", note)
end

# Formats a result for which Roast has no dedicated formatter.
#
# Content: the tool's output text.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,30 @@ class ToolCallMessageTest < ActiveSupport::TestCase
assert_equal "GREP \"#{"x" * (ToolCallMessage::TRUNCATE_LIMIT - 3)}...\" #{long}", msg.format
end

test "format renders FIND with the search path but no limit" do
msg = ToolCallMessage.new(
id: "1",
name: "find",
arguments: { pattern: "*.rb", path: "lib" },
)
assert_equal "FIND *.rb (path: lib)", msg.format
end

test "format renders FIND with the path and limit joined in one parenthetical" do
msg = ToolCallMessage.new(id: "1", name: "find", arguments: { pattern: "*.rb", path: "lib", limit: 50 })
assert_equal "FIND *.rb (path: lib, limit: 50)", msg.format
end

test "format renders FIND with the limit but no path" do
msg = ToolCallMessage.new(id: "1", name: "find", arguments: { pattern: "*.rb", limit: 50 })
assert_equal "FIND *.rb (limit: 50)", msg.format
end

test "format renders FIND with only the pattern" do
msg = ToolCallMessage.new(id: "1", name: "find", arguments: { pattern: "*.rb" })
assert_equal "FIND *.rb", msg.format
end

test "format renders an unhandled tool as NAME key: value, ..." do
msg = ToolCallMessage.new(
id: "1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,64 @@ def setup
assert_equal "GREP OK 1 match · NOTE results truncated", msg.format(@context)
end

test "format summarizes find output with a path count" do
msg = ToolResultMessage.new(
tool_call_id: "1",
tool_name: "find",
content: "lib/roast.rb\nlib/roast/version.rb\nlib/roast/cog.rb",
is_error: false,
)

assert_equal "FIND OK 3 paths", msg.format(@context)
end

test "format pluralizes a single find path" do
msg = ToolResultMessage.new(
tool_call_id: "1",
tool_name: "find",
content: "lib/roast.rb",
is_error: false,
)

assert_equal "FIND OK 1 path", msg.format(@context)
end

test "format reports zero find paths when there is no output" do
msg = ToolResultMessage.new(
tool_call_id: "1",
tool_name: "find",
content: nil,
is_error: false,
)

assert_equal "FIND OK 0 paths", msg.format(@context)
end

test "format keeps a find limit notice out of the path count and appends it as a NOTE" do
notice = "[2 results limit reached. Use limit=4 for more, or refine pattern]"
msg = ToolResultMessage.new(
tool_call_id: "1",
tool_name: "find",
content: "file4.txt\nfile5.txt\n\n#{notice}",
is_error: false,
)

unbracketed = notice.delete_prefix("[").delete_suffix("]")
truncated_notice = "#{unbracketed[0...ToolResultMessage::TRUNCATE_LIMIT - 3]}..."
assert_equal "FIND OK 2 paths · NOTE #{truncated_notice}", msg.format(@context)
end

test "format reports zero find paths when the output is the no-results notice" do
msg = ToolResultMessage.new(
tool_call_id: "1",
tool_name: "find",
content: "No files found matching pattern",
is_error: false,
)

assert_equal "FIND OK 0 paths", msg.format(@context)
end

test "format renders NAME ERROR with the message for an error result" do
msg = ToolResultMessage.new(
tool_call_id: "1",
Expand Down
Loading