Class: DrainJob

Inherits:
ApplicationJob show all
Defined in:
app/jobs/drain_job.rb

Overview

Drains the PendingMessage mailbox into a single LLM round-trip.

One invocation == one half-step of the event-driven agent loop:

  1. Claim the session via Session#start_processing! (atomic; bails if another drain already holds the session OR the in-flight tool round is incomplete — the AASM guard tool_round_complete? handles both).

  2. Promote pending work into the conversation — every tool_response PM of the freshly completed round flushes in one transaction so the LLM sees a whole assistant turn paired with a whole user turn; background phantom pairs flush; one active FIFO message rides along. Promotion lives on PendingMessage#promote! — the job only decides what to pick and in which order.

  3. Make one LLM API call and emit Events::LLMResponded.

On the happy path the job never releases the session — state transitions after the emit belong to Events::Subscribers::LLMResponseHandler (on text or tool dispatch). Events::Subscribers::ToolResponseCreator no longer touches state; the executing → awaiting branch of start_processing closes the tool round and claims in one atomic, lock-protected step.

The job DOES release its own claim when there is no responder to do it: an empty mailbox (spurious kickoff) or an exception raised before the LLM call succeeded. Those are lifecycle edges of the claim itself, not hand-offs to responders.

Instance Method Summary collapse

Instance Method Details

#perform(session_id) ⇒ Object

Parameters:

  • session_id (Integer)


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/jobs/drain_job.rb', line 49

def perform(session_id)
  @session = Session.find(session_id)
  return unless @session.start_processing!

  drained = drain_mailbox
  return @session.response_complete! if drained.zero?

  call_llm_and_emit
rescue Providers::Anthropic::AuthenticationError => error
  release_after_failure(error) if @session
  raise
rescue => error
  release_after_failure(error) if @session
  raise unless @active_pm&.bounce_back?
end