Class: Rubino::Interaction::Lifecycle
- Inherits:
-
Object
- Object
- Rubino::Interaction::Lifecycle
- Defined in:
- lib/rubino/interaction/lifecycle.rb
Overview
Orchestrates the full lifecycle of a single user interaction. Coordinates all phases from input to final response and post-turn jobs.
Instance Method Summary collapse
-
#active_session ⇒ Object
The session this lifecycle is currently bound to.
-
#execute(input, image_paths: [], input_queue: nil, paste_expansions: []) ⇒ Object
Executes the full interaction lifecycle for a user input.
-
#initialize(session:, event_bus:, ui:, config:, ignore_rules: false, agent_definition: nil, cancel_token: nil, model_override: nil, provider_override: nil, max_tool_iterations: nil, polishing: nil) ⇒ Lifecycle
constructor
A new instance of Lifecycle.
Constructor Details
#initialize(session:, event_bus:, ui:, config:, ignore_rules: false, agent_definition: nil, cancel_token: nil, model_override: nil, provider_override: nil, max_tool_iterations: nil, polishing: nil) ⇒ Lifecycle
Returns a new instance of Lifecycle.
18 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 |
# File 'lib/rubino/interaction/lifecycle.rb', line 18 def initialize(session:, event_bus:, ui:, config:, ignore_rules: false, agent_definition: nil, cancel_token: nil, model_override: nil, provider_override: nil, max_tool_iterations: nil, polishing: nil) @session = session @event_bus = event_bus @ui = ui @config = config @ignore_rules = ignore_rules @agent_definition = agent_definition @cancel_token = cancel_token @model_override = model_override @provider_override = provider_override # The Runner-owned detached post-turn polishing worker (#319). When # given, the post-turn jobs are handed to it to drain OFF the live # turn's critical path so the next prompt is never gated. Nil on the # API/server path and nested subagent runs, which keep the original # synchronous inline drain (no interactive prompt to free up). @polishing = polishing # Explicit per-run cap from `--max-turns` (Runner → here → IterationBudget). # nil ⇒ use the configured agent_max_tool_iterations (#141). @max_tool_iterations = max_tool_iterations @state = State.new @session_repo = Session::Repository.new @message_store = Session::Store.new end |
Instance Method Details
#active_session ⇒ Object
The session this lifecycle is currently bound to. Starts as the session passed in, but an automatic budget-triggered compaction swaps it to the compaction child (see #check_and_compact). The owning Runner reads this back after #execute so the NEXT turn runs on the (small) child rather than re-compacting the dead parent every turn (P3 F1). Defined as a method (not attr_reader) because @session is REASSIGNED on compaction.
14 15 16 |
# File 'lib/rubino/interaction/lifecycle.rb', line 14 def active_session @session end |
#execute(input, image_paths: [], input_queue: nil, paste_expansions: []) ⇒ Object
Executes the full interaction lifecycle for a user input. image_paths are vision-capable attachments routed natively to the primary model (ruby_llm ‘with:` slot); only consumed on the first iteration of the inner agent loop. Subsequent iterations carry tool results, not user input, and don’t re-attach the images. input_queue is the optional steering hand-off (Interaction::InputQueue) for mid-turn injection: when given, the inner agent loop drains any text the user typed while it was working and folds it into the turn at a safe iteration boundary. Nil for the API/server path and for nested SUBAGENT runs, which stay isolated — no user injection, exactly as before.
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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/rubino/interaction/lifecycle.rb', line 55 def execute(input, image_paths: [], input_queue: nil, paste_expansions: []) @event_bus.emit(Events::INTERACTION_STARTED, input: input) @state.transition_to!(:receiving_input, event_bus: @event_bus) # 1. Persist user message @state.transition_to!(:loading_session, event_bus: @event_bus) (input, paste_expansions: paste_expansions) # 2. Load memory (if enabled) @state.transition_to!(:loading_memory, event_bus: @event_bus) memory_context = load_memory(input) # 3. Build prompt/context @state.transition_to!(:building_context, event_bus: @event_bus) = (input, memory_context) tools = load_tools # 4. Check token budget @state.transition_to!(:checking_budget, event_bus: @event_bus) = check_and_compact() # 5. Run agent loop @state.transition_to!(:calling_model, event_bus: @event_bus) response = run_agent_loop(, tools, image_paths: image_paths, input_queue: input_queue) # 6. Persist session state @state.transition_to!(:persisting_session, event_bus: @event_bus) update_session_state # 7. Enqueue post-turn jobs @state.transition_to!(:enqueueing_jobs, event_bus: @event_bus) enqueue_post_turn_jobs # 8. Finish # Carry the final assistant text as the terminal event's authoritative # output, regardless of streaming mode. Streaming consumers also receive # it incrementally via MODEL_STREAM (message.delta), but the # non-streaming path emits no deltas — so without this, a completed run # would terminate with no final text for clients to display. This makes # run.completed the single source of truth for the answer. @state.transition_to!(:finished, event_bus: @event_bus) @event_bus.emit(Events::INTERACTION_FINISHED, output: response.to_s) response rescue StandardError => e @state.transition_to!(:failed, event_bus: @event_bus) @event_bus.emit(Events::INTERACTION_FAILED, error: e.) raise end |