class RubyLLM::ResearchJob
A hosted, single-turn research task. RubyLLM.research_later returns a job immediately; wait polls it and message returns its report. The provider’s agent identity is separate from an inference model.
job = RubyLLM.research_later(question, provider: provider, agent: agent_id) job.wait puts job.message.content
Attributes
The hosted agent’s identity, separate from a model ID.
The error from an unsuccessful automatic cancellation attempt, if any.
The provider’s failure explanation, if any.
The provider-assigned job ID.
The provider slug.
The original provider response from submission or the latest poll.
The normalized state: :pending, :completed, :incomplete, :failed, or :cancelled.
Returns the provider-reported task usage. Unreported fields are nil.
Public Class Methods
# File lib/ruby_llm/research_job.rb, line 110 def self.find(id, provider:, context: nil) config = context&.config || RubyLLM.config Provider.resolve!(provider).new(config).find_research_job(id) end
Retrieves an existing task by ID without submitting another one. Use the same provider configuration that created the task.
# File lib/ruby_llm/research_job.rb, line 88 def self.research(prompt, timeout: 600, interval: 5, **options) validate_polling_options(timeout, interval) job = research_later(prompt, **options) job.wait(timeout:, interval:).message rescue Interrupt => e raise unless job job.send(:attempt_cancellation) raise InterruptedError.new("Research interrupted (job #{job.id})", job:), cause: e rescue StandardError => e job ||= e.job if e.is_a?(Error) raise unless job job.send(:attempt_cancellation) raise if e.is_a?(Error) response = e.response if e.respond_to?(:response) raise Error.new("Research failed: #{e.message} (job #{job.id})", job:, response:), cause: e end
Runs a research task and returns its Message. On a timeout or interrupt, attempts to cancel the remote task before raising an error retaining the job. timeout: and interval: are seconds.
# File lib/ruby_llm/research_job.rb, line 72 def self.research_later(prompt, provider:, agent:, with: nil, server_tools: nil, context: nil, provider_options: {}, metadata: nil) config = context&.config || RubyLLM.config instance = Provider.resolve!(provider).new(config) payload = { provider: instance.slug, agent:, prompt:, metadata: } RubyLLM.instrument('research_job.ruby_llm', payload, config:) do |event| job = instance.research_later(prompt, agent:, with:, server_tools:, provider_options:) event[:job_id] = job.id event[:status] = job.status job end end
Submits one research task without waiting. provider: and agent: are required; with: attaches documents or images where supported. server_tools: accepts an array of aliases or a Hash of aliases and their options, as on Chat#with_server_tools.
Public Instance Methods
Source
# File lib/ruby_llm/research_job.rb, line 178 def cancel(timeout: 5) self.class.validate_polling_options(timeout, 1) unless done? state = request_with_timeout(timeout) { @protocol.cancel_research_job(self, timeout:) } apply_state(state) end self end
Requests cancellation and returns self. Only the provider’s response can confirm cancellation; this does not delete stored task data.
Source
# File lib/ruby_llm/research_job.rb, line 145 def cancelled? = status == :cancelled
Returns whether the provider confirmed cancellation.
Source
# File lib/ruby_llm/research_job.rb, line 136 def completed? = status == :completed
Returns whether the task finished with a complete report.
Source
# File lib/ruby_llm/research_job.rb, line 201 def cost Cost.from_h({ total: tokens.reported_cost }.compact, tokens:) end
Returns the reported cost, or unknown cost when the provider supplies no price. Agent IDs are never used to look up model token prices.
Source
# File lib/ruby_llm/research_job.rb, line 133 def done? = !pending?
Returns whether the task reached any terminal state.
Source
# File lib/ruby_llm/research_job.rb, line 142 def failed? = status == :failed
Returns whether the provider reported failure.
Source
# File lib/ruby_llm/research_job.rb, line 139 def incomplete? = status == :incomplete
Returns whether the provider stopped before completing the report.
Source
# File lib/ruby_llm/research_job.rb, line 190 def message raise Error.new("Research #{status}: #{error} (job #{id})", job: self) if failed? || cancelled? @message end
Returns the report, or nil while pending. An incomplete report has Message#finish_reason :max_tokens. Raises Error for failed or cancelled tasks.
Source
# File lib/ruby_llm/research_job.rb, line 130 def pending? = status == :pending
Returns whether the task is waiting or running.
Source
# File lib/ruby_llm/research_job.rb, line 149 def refresh(timeout: nil) self.class.validate_polling_options(timeout, 1) unless timeout.nil? unless done? state = request_with_timeout(timeout) { @protocol.refresh_research_job(self, timeout:) } apply_state(state) end self end
Fetches the latest state and returns self. Does nothing after the task finishes. timeout: limits this request in seconds.
# File lib/ruby_llm/research_job.rb, line 161 def wait(timeout: 600, interval: 5) self.class.validate_polling_options(timeout, interval) deadline = monotonic_time + timeout until done? remaining = deadline - monotonic_time raise TimeoutError.new("Research timed out (job #{id})", job: self) unless remaining.positive? refresh(timeout: remaining) sleep [interval, deadline - monotonic_time].min if pending? && monotonic_time < deadline end raise Error.new("Research #{status}: #{error} (job #{id})", job: self) if failed? || cancelled? self end
Polls until a terminal state and returns self. A timeout leaves the independent task running and raises TimeoutError with this job. Incomplete reports remain available through message.