Skip to content
Open
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
34 changes: 34 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 @@ -63,6 +63,40 @@ def format_bash
command.empty? ? "BASH" : "BASH #{command}"
end

# Formats a read tool call.
#
# Input fields:
# :path (String) – file to read [required]
# :offset (Integer) – 1-indexed first line [optional]
# :limit (Integer) – maximum number of lines [optional]
#
# Output: "READ <path>", with a line range appended when :offset and/or
# :limit is given:
# :limit >= 1 → " (lines <start>–<end>)", start = :offset (default 1),
# end = start + :limit - 1
# :offset only → " (from line <offset>)" (reads to end of file); also the
# fallback when :limit <= 0, which has no sensible range
# With neither, the bare "READ <path>". A missing path renders "READ".
#
# Examples:
# READ lib/roast.rb (lines 30–80)
# READ lib/roast.rb (from line 30)
# READ lib/roast.rb
#
#: () -> String
def format_read
path, offset, limit = arguments.values_at(:path, :offset, :limit)
path = path.to_s
details = if limit&.positive?
offset ||= 1
"lines #{offset}–#{offset + limit - 1}"
Comment thread
LasmarKhalifa marked this conversation as resolved.
elsif offset
"from line #{offset}"
end
label = path.empty? ? "READ" : "READ #{path}"
details ? "#{label} (#{details})" : label
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 @@ -76,6 +76,26 @@ def format_bash
ok_line("#{count} #{"line".pluralize(count)}", preview)
end

# Formats a read tool result.
#
# Content: the file's text.
#
# Output: "READ OK <n> <line|lines>" – <n> is the file's line count
# (pluralized). Blank lines are counted: this is the file's own length, so
# it deliberately differs from entry-listing tools (find/ls), which drop
# blanks because a blank isn't an entry.
#
# Examples:
# READ OK 42 lines
# READ OK 1 line
# READ OK 0 lines
#
#: () -> String
def format_read
Comment thread
LasmarKhalifa marked this conversation as resolved.
count = content.to_s.lines.length
ok_line("#{count} #{"line".pluralize(count)}")
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 @@ -26,6 +26,41 @@ class ToolCallMessageTest < ActiveSupport::TestCase
assert_equal "BASH #{"x" * (ToolCallMessage::TRUNCATE_LIMIT - 3)}...", msg.format
end

test "format renders READ with the path" do
msg = ToolCallMessage.new(id: "1", name: "read", arguments: { path: "lib/roast.rb" })
assert_equal "READ lib/roast.rb", msg.format
end

test "format renders a bare READ when the path is missing" do
msg = ToolCallMessage.new(id: "1", name: "read", arguments: {})
assert_equal "READ", msg.format
end

test "format renders READ with a closed line range when offset and limit are set" do
msg = ToolCallMessage.new(id: "1", name: "read", arguments: { path: "lib/roast.rb", offset: 30, limit: 51 })
assert_equal "READ lib/roast.rb (lines 30–80)", msg.format
end

test "format defaults the read offset to 1 when only limit is given" do
msg = ToolCallMessage.new(id: "1", name: "read", arguments: { path: "lib/roast.rb", limit: 50 })
assert_equal "READ lib/roast.rb (lines 1–50)", msg.format
end

test "format renders an open-ended read range from offset when limit is absent" do
msg = ToolCallMessage.new(id: "1", name: "read", arguments: { path: "lib/roast.rb", offset: 30 })
assert_equal "READ lib/roast.rb (from line 30)", msg.format
end

test "format falls back to the open-ended range when read limit is non-positive" do
msg = ToolCallMessage.new(id: "1", name: "read", arguments: { path: "lib/roast.rb", offset: 30, limit: 0 })
assert_equal "READ lib/roast.rb (from line 30)", msg.format
end

test "format omits the range entirely when read limit is non-positive and no offset is given" do
msg = ToolCallMessage.new(id: "1", name: "read", arguments: { path: "lib/roast.rb", limit: 0 })
assert_equal "READ lib/roast.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 @@ -43,6 +43,39 @@ def setup
assert_equal "BASH OK 0 lines", msg.format(@context)
end

test "format summarizes read output with a line count" do
msg = ToolResultMessage.new(
tool_call_id: "1",
tool_name: "read",
content: "line one\nline two\nline three",
is_error: false,
)

assert_equal "READ OK 3 lines", msg.format(@context)
end

test "format pluralizes a single line of read output" do
msg = ToolResultMessage.new(
tool_call_id: "1",
tool_name: "read",
content: "just one line",
is_error: false,
)

assert_equal "READ OK 1 line", msg.format(@context)
end

test "format reports zero lines when the file is empty" do
msg = ToolResultMessage.new(
tool_call_id: "1",
tool_name: "read",
content: nil,
is_error: false,
)

assert_equal "READ OK 0 lines", 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