Module: Riffer::Agent::Session::Repair
Overview
Pure, stateless transformations keeping the tool_use ↔ tool_result invariant on a message array. Each entry point no-ops when Riffer.config.experimental_history_healing is off.
Constant Summary collapse
- ORPHAN_PLACEHOLDER =
Placeholder response filled in for an orphaned
tool_useon interrupt. ->(_tool_call) { Riffer::Tools::Response.error("Tool call interrupted before completion.", type: :interrupted) }
Instance Method Summary collapse
-
#fill_orphans(messages) ⇒ Object
Fills each orphaned
tool_useinmessageswith anORPHAN_PLACEHOLDERresult inserted after its parent. -
#prune_orphans(messages) ⇒ Object
Prunes a seeded message array to the invariant — dropping orphaned tool exchanges and parentless Tool messages, but preserving the pending tool_calls on the resume boundary (the last assistant) for
execute_pending_tool_calls.
Instance Method Details
#fill_orphans(messages) ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/riffer/agent/session/repair.rb', line 19 def fill_orphans() return [, []] unless Riffer.config.experimental_history_healing result_ids = .filter_map { |m| m.tool_call_id if m.is_a?(Riffer::Messages::Tool) } filled = [] #: Array[String] = [] #: Array[Riffer::Messages::Base] .each do |m| << m next unless m.is_a?(Riffer::Messages::Assistant) && !m.tool_calls.empty? m.tool_calls.each do |tc| next if result_ids.include?(tc.call_id) response = ORPHAN_PLACEHOLDER.call(tc) << Riffer::Messages::Tool.new( response.content, tool_call_id: tc.call_id, name: tc.name, error: response., error_type: response.error_type ) filled << tc.call_id end end [, filled] end |
#prune_orphans(messages) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/riffer/agent/session/repair.rb', line 54 def prune_orphans() return unless Riffer.config.experimental_history_healing resume_boundary = (.length - 1).downto(0).find { |idx| m = [idx] m.is_a?(Riffer::Messages::Assistant) && ([(idx + 1)..] || []).all? { |later| later.is_a?(Riffer::Messages::Tool) } } result_ids = .filter_map { |m| m.tool_call_id if m.is_a?(Riffer::Messages::Tool) } parent_ids = .flat_map { |m| m.is_a?(Riffer::Messages::Assistant) ? m.tool_calls.map(&:call_id) : [] } strip_offenders = .each_with_index.flat_map { |m, idx| next [] unless m.is_a?(Riffer::Messages::Assistant) && !m.tool_calls.empty? next [] if idx == resume_boundary # preserve pending exchange next [] if m.tool_calls.all? { |tc| result_ids.include?(tc.call_id) } m.tool_calls.map(&:call_id) } .reject { |m| case m when Riffer::Messages::Assistant !m.tool_calls.empty? && m.tool_calls.any? { |tc| strip_offenders.include?(tc.call_id) } when Riffer::Messages::Tool strip_offenders.include?(m.tool_call_id) || !parent_ids.include?(m.tool_call_id) else false end } end |