Class: Raif::Task

Direct Known Subclasses

Evals::LlmJudge

Constant Summary

Constants included from Concerns::LlmResponseParsing

Concerns::LlmResponseParsing::ASCII_CONTROL_CHARS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Concerns::RunWith

deserialize_run_with_value, gid_string?, locate_gid, serialize_run_with_value

Methods included from Concerns::JsonSchemaDefinition

#schema_for_instance

Methods included from Concerns::LlmResponseParsing

#parse_html_response, #parse_json_response, #parsed_response

Methods included from Concerns::HasRuntimeDuration

#runtime_duration, #runtime_duration_seconds, #runtime_ended_at

Methods included from Concerns::HasAvailableModelTools

#available_model_tools_map

Methods included from Concerns::HasRequestedLanguage

#requested_language_name, #system_prompt_language_preference

Methods included from Concerns::HasLlm

#default_llm_model_key, #llm

Instance Attribute Details

#filesObject

Returns the value of attribute files.



77
78
79
# File 'app/models/raif/task.rb', line 77

def files
  @files
end

#imagesObject

Returns the value of attribute images.



77
78
79
# File 'app/models/raif/task.rb', line 77

def images
  @images
end

Class Method Details

.build_for_batch(batch:, batch_custom_id: nil, creator: nil, available_model_tools: [], llm_model_key: nil, images: [], files: [], **args) ⇒ Raif::Task

Builds a Raif::Task and a pending Raif::ModelCompletion attached to the given Raif::ModelCompletionBatch, without performing the LLM request. The provider's batch-submission code reads the pending completion's request fields when constructing its per-entry batch payload.

The task is persisted with started_at: nil (state :pending). The pending ModelCompletion has raif_model_completion_batch_id pointing at the batch and batch_custom_id set to the value used as the provider's custom_id in the batch payload.

Parameters:

  • batch (Raif::ModelCompletionBatch)
  • batch_custom_id (String, nil) (defaults to: nil)

    the provider custom_id; defaults to "raif_task_<task.id>"

Returns:



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'app/models/raif/task.rb', line 223

def self.build_for_batch(batch:, batch_custom_id: nil, creator: nil, available_model_tools: [],
  llm_model_key: nil, images: [], files: [], **args)
  task = build_task_instance(
    creator: creator,
    available_model_tools: available_model_tools,
    llm_model_key: llm_model_key,
    images: images,
    files: files,
    **args
  )

  task.save!
  task.prepare_for_batch!(batch: batch, batch_custom_id: batch_custom_id)
  task
end

.find_sti_class(type_name) ⇒ Object

A task's subclass lives in the host application and can be deleted while its rows remain. Reading such a row must not raise: it is the oldest rows that are most likely to name a class that is gone, and those are exactly the ones Raif::ArchiveTasksJob has to serialize and the admin has to list. The row's own columns are all that either path needs.



87
88
89
90
91
# File 'app/models/raif/task.rb', line 87

def self.find_sti_class(type_name)
  super
rescue ActiveRecord::SubclassNotFound
  Raif::Task
end

.json_response_schema(&block) ⇒ Object



323
324
325
326
327
328
329
# File 'app/models/raif/task.rb', line 323

def self.json_response_schema(&block)
  if block_given?
    json_schema_definition(:json_response, &block)
  elsif schema_defined?(:json_response)
    schema_for(:json_response)
  end
end

.prompt(creator: nil, **args) ⇒ String

Returns the LLM prompt for the task.

Parameters:

  • creator (Object, nil) (defaults to: nil)

    The creator of the task (polymorphic association), optional

  • args (Hash)

    Additional arguments to pass to the instance of the task that is created.

Returns:

  • (String)

    The LLM prompt for the task.



310
311
312
# File 'app/models/raif/task.rb', line 310

def self.prompt(creator: nil, **args)
  new(creator:, **args).build_prompt
end

.run(creator: nil, available_model_tools: [], llm_model_key: nil, images: [], files: [], **args, &block) ⇒ Raif::Task?

The primary interface for running a task. It will hit the LLM with the task's prompt and system prompt and return a Raif::Task object. It will also create a new Raif::ModelCompletion record.

Parameters:

  • creator (Object, nil) (defaults to: nil)

    The creator of the task (polymorphic association), optional

  • available_model_tools (Array<Class>) (defaults to: [])

    Optional array of model tool classes that will be provided to the LLM for it to invoke.

  • llm_model_key (Symbol, String) (defaults to: nil)

    Optional key for the LLM model to use. If blank, Raif.config.default_llm_model_key will be used.

  • images (Array) (defaults to: [])

    Optional array of Raif::ModelImageInput objects to include with the prompt.

  • files (Array) (defaults to: [])

    Optional array of Raif::ModelFileInput objects to include with the prompt.

  • args (Hash)

    Additional arguments to pass to the instance of the task that is created.

Returns:

  • (Raif::Task, nil)

    The task instance that was created and run.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'app/models/raif/task.rb', line 115

def self.run(creator: nil, available_model_tools: [], llm_model_key: nil, images: [], files: [], **args, &block)
  task = build_task_instance(
    creator: creator,
    available_model_tools: available_model_tools,
    llm_model_key: llm_model_key,
    images: images,
    files: files,
    started_at: Time.current,
    **args
  )

  task.save!
  task.run(&block)
  task
rescue StandardError => e
  task&.failed!

  # An authorization veto is intentional, not a model failure - re-raise it
  # to the caller instead of logging/reporting it as an error.
  raise if e.is_a?(Raif::Errors::ModelCompletionAuthorizationError)

  logger.error e.message
  logger.error e.backtrace.join("\n")

  if defined?(Airbrake)
    notice = Airbrake.build_notice(e)
    notice[:context][:component] = "raif_task"
    notice[:context][:action] = name

    Airbrake.notify(notice)
  end

  task
end

.system_prompt(creator: nil, **args) ⇒ String

Returns the LLM system prompt for the task.

Parameters:

  • creator (Object, nil) (defaults to: nil)

    The creator of the task (polymorphic association), optional

  • args (Hash)

    Additional arguments to pass to the instance of the task that is created.

Returns:

  • (String)

    The LLM system prompt for the task.



319
320
321
# File 'app/models/raif/task.rb', line 319

def self.system_prompt(creator: nil, **args)
  new(creator:, **args).build_system_prompt
end

Instance Method Details

#build_promptObject

Raises:

  • (NotImplementedError)


347
348
349
# File 'app/models/raif/task.rb', line 347

def build_prompt
  raise NotImplementedError, "Raif::Task subclasses must implement #build_prompt"
end

#build_system_promptObject



351
352
353
354
355
356
# File 'app/models/raif/task.rb', line 351

def build_system_prompt
  sp = Raif.config.task_system_prompt_intro
  sp = sp.call(self) if sp.respond_to?(:call)
  sp += system_prompt_language_preference if requested_language_key.present?
  sp
end

#json_response_schemaObject

Instance method to get the JSON response schema For instance-dependent schemas, builds the schema with this instance as context For class-level schemas, returns the class-level schema



334
335
336
# File 'app/models/raif/task.rb', line 334

def json_response_schema
  schema_for_instance(:json_response)
end

#messagesObject



301
302
303
# File 'app/models/raif/task.rb', line 301

def messages
  [{ "role" => "user", "content" => message_content }]
end

#prepare_for_batch!(batch:, batch_custom_id: nil, skip_prompt_population: false) ⇒ Raif::ModelCompletion

Populates this task's prompts and creates the pending Raif::ModelCompletion that will be sent through the batch. Mirrors Raif::Task#run's default behavior: prompts are rebuilt every call unless skip_prompt_population: true is passed (e.g. for re-prepare flows where the caller has hand-set prompts and wants them preserved).

The default batch_custom_id is "raif_task_<task.id>" — a task-specific identifier. Other producers (non-task) should pass an explicit batch_custom_id when building their own pending completions via Raif::Llm#build_pending_model_completion; the only requirement is uniqueness within the batch.

Parameters:

  • batch (Raif::ModelCompletionBatch)
  • batch_custom_id (String, nil) (defaults to: nil)
  • skip_prompt_population (Boolean) (defaults to: false)

    preserve any pre-set prompts on this task instead of rebuilding via #build_prompt / #build_system_prompt

Returns:



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'app/models/raif/task.rb', line 275

def prepare_for_batch!(batch:, batch_custom_id: nil, skip_prompt_population: false)
  populate_prompts unless skip_prompt_population
  save! if changed?

  effective_batch_custom_id = batch_custom_id.presence || "raif_task_#{id}"

  mc = llm.build_pending_model_completion(
    messages: messages,
    source: self,
    system_prompt: system_prompt,
    response_format: response_format.to_sym,
    available_model_tools: available_model_tools,
    temperature: self.class.temperature,
    anthropic_prompt_caching_enabled: self.class.anthropic_prompt_caching_enabled,
    bedrock_prompt_caching_enabled: self.class.bedrock_prompt_caching_enabled,
    open_ai_store_responses: self.class.open_ai_store_responses,
    open_router_data_collection: self.class.open_router_data_collection,
    open_router_zdr: self.class.open_router_zdr,
    raif_model_completion_batch: batch,
    batch_custom_id: effective_batch_custom_id
  )

  self.raif_model_completion = mc
  mc
end

#process_completion!(model_completion) ⇒ self

Wires a (possibly batch-resolved) Raif::ModelCompletion back into this task: links it as the task's raif_model_completion, mirrors raw_response onto the task, processes any tool calls the model made, and transitions the task to completed (or failed, if the underlying completion failed). Called by both the synchronous #run path and the batch completion handler after the provider's per-entry result has been applied.

Parameters:

Returns:

  • (self)


194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/models/raif/task.rb', line 194

def process_completion!(model_completion)
  self.raif_model_completion = model_completion.becomes(Raif::ModelCompletion)

  update(raw_response: raif_model_completion.raw_response)

  if raif_model_completion.failed?
    # Failure detail (failure_error / failure_reason) lives on the model
    # completion; the task's `failed_at` is just a state marker.
    failed!
  else
    process_model_tool_invocations
    completed!
  end
  self
end

#prompt_studio_task_attributesHash

Returns additional attributes to assign when creating tasks in Prompt Studio (reruns, batch runs, and judge tasks). Override in your ApplicationTask or task subclass to include app-specific attributes.

Returns:

  • (Hash)

    additional attributes to assign to the new task



343
344
345
# File 'app/models/raif/task.rb', line 343

def prompt_studio_task_attributes
  {}
end

#re_run(&block) ⇒ Object



180
181
182
183
# File 'app/models/raif/task.rb', line 180

def re_run(&block)
  update_columns(started_at: Time.current)
  run(skip_prompt_population: true, &block)
end

#run(skip_prompt_population: false, &block) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'app/models/raif/task.rb', line 150

def run(skip_prompt_population: false, &block)
  update_columns(started_at: Time.current) if started_at.nil?

  populate_prompts unless skip_prompt_population

  streaming_block = if block_given?
    proc do |model_completion, delta, sse_event|
      update_columns(raw_response: model_completion.raw_response, updated_at: Time.current)
      block.call(model_completion, delta, sse_event)
    end
  end

  mc = llm.chat(
    messages: messages,
    source: self,
    system_prompt: system_prompt,
    response_format: response_format.to_sym,
    available_model_tools: available_model_tools,
    temperature: self.class.temperature,
    anthropic_prompt_caching_enabled: self.class.anthropic_prompt_caching_enabled,
    bedrock_prompt_caching_enabled: self.class.bedrock_prompt_caching_enabled,
    open_ai_store_responses: self.class.open_ai_store_responses,
    open_router_data_collection: self.class.open_router_data_collection,
    open_router_zdr: self.class.open_router_zdr,
    &streaming_block
  )

  process_completion!(mc)
end

#statusObject



93
94
95
96
97
98
99
100
101
102
103
# File 'app/models/raif/task.rb', line 93

def status
  if completed_at?
    :completed
  elsif failed_at?
    :failed
  elsif started_at?
    :in_progress
  else
    :pending
  end
end