Class: Rubino::Context::ToolPairSanitizer

Inherits:
Object
  • Object
show all
Defined in:
lib/rubino/context/tool_pair_sanitizer.rb

Overview

Ensures tool_call and tool_result pairs are not split during compaction. If a tool_call is in the compressible section, its result must be too (and vice versa).

WIRE FORMAT: an assistant tool call lives in metadata (a list of { id:, name:, arguments: }), NOT in tool_call_id — tool_call_id is only set on role:“tool” RESULT rows. The original predicate keyed off ‘tool_call_id && role==“assistant”`, a contradiction that never matched, so the trailing-orphan trim was inert. The methods below read metadata, and are also reused by PromptAssembler’s pre-send repair pass.

Instance Method Summary collapse

Instance Method Details

#assistant_tool_call?(message) ⇒ Boolean

True when the message is an assistant turn carrying tool calls.

Returns:

  • (Boolean)


32
33
34
35
36
# File 'lib/rubino/context/tool_pair_sanitizer.rb', line 32

def assistant_tool_call?(message)
  message.role == "assistant" &&
    message.respond_to?(:metadata) && message..is_a?(Hash) &&
    !Array(message.[:tool_calls]).empty?
end

#sanitize(middle_messages) ⇒ Object

Adjusts a slice to ensure tool pairs remain intact at its boundaries.



17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/rubino/context/tool_pair_sanitizer.rb', line 17

def sanitize(middle_messages)
  adjusted = middle_messages.dup

  # Leading orphan: a tool RESULT whose call lives in the head section.
  adjusted.shift while adjusted.first&.role == "tool"

  # Trailing orphan: an assistant tool call whose results are NOT all
  # present after it in this slice (e.g. interrupted turn, or results
  # landed in the tail section). A fully-PAIRED trailing call is kept.
  adjusted.pop while adjusted.last && trailing_unanswered_tool_call?(adjusted)

  adjusted
end

#tool_call_ids(message) ⇒ Object

The tool_call ids declared by an assistant message. Handles both symbol and string keys — metadata is hydrated with symbolize_names but in-memory messages (pre-persist) may carry string keys.



41
42
43
# File 'lib/rubino/context/tool_pair_sanitizer.rb', line 41

def tool_call_ids(message)
  Array(message.[:tool_calls]).map { |tc| tc[:id] || tc["id"] }.compact
end