Class: Raif::InferenceCostEvent
- Inherits:
-
ApplicationRecord
- Object
- ApplicationRecord
- Raif::InferenceCostEvent
- Defined in:
- app/models/raif/inference_cost_event.rb
Overview
A slim, durable record of the cost/token usage of a single model completion. Created automatically when a Raif::ModelCompletion reaches a terminal state (completed or failed) and retained after the completion row (and, later, its source) is deleted, so cost reporting never depends on fat completion rows sticking around.
Schema Information
Table name: raif_inference_cost_events
id :bigint not null, primary key cache_creation_input_tokens :integer cache_read_input_tokens :integer completion_completed_at :datetime completion_failed_at :datetime completion_tokens :integer incurred_at :datetime not null llm_model_key :string not null metadata :jsonb model_api_name :string not null output_token_cost :decimal(10, 6) prompt_token_cost :decimal(10, 6) prompt_tokens :integer retry_count :integer default(0), not null source_class_name :string source_type :string total_cost :decimal(10, 6) total_tokens :integer created_at :datetime not null updated_at :datetime not null original_model_completion_id :bigint not null raif_archive_id :bigint raif_model_completion_batch_id :bigint raif_model_completion_id :bigint raif_task_archive_id :bigint source_id :bigint
Indexes
index_raif_inference_cost_events_on_incurred_at (incurred_at) index_raif_inference_cost_events_on_original_completion_id (original_model_completion_id) index_raif_inference_cost_events_on_raif_archive_id (raif_archive_id) index_raif_inference_cost_events_on_raif_model_completion_id (raif_model_completion_id) UNIQUE index_raif_inference_cost_events_on_raif_task_archive_id (raif_task_archive_id) index_raif_inference_cost_events_on_source_type_and_source_id (source_type,source_id) index_raif_inference_cost_events_on_source_type_incurred_at (source_type,incurred_at)
Foreign Keys
fk_rails_... (raif_model_completion_id => raif_model_completions.id) ON DELETE => nullify
Class Method Summary collapse
-
.backfill!(batch_size: 500) ⇒ Object
Creates events for terminal completions that don't have one yet, and re-syncs events that are STALE (older than their completion: a post-terminal completion update committed but its event re-sync failed).
Instance Method Summary collapse
-
#model_completion_live? ⇒ Boolean
Column check, not a query: the FK is nullified at the DB level the moment the completion row is deleted.
Class Method Details
.backfill!(batch_size: 500) ⇒ Object
Creates events for terminal completions that don't have one yet, and re-syncs events that are STALE (older than their completion: a post-terminal completion update committed but its event re-sync failed). The bulk one-time entry point after upgrading (see the raif:backfill_inference_cost_events rake task); Raif::RepairInferenceCostEventsJob runs the same operation as steady-state self-healing. Terminal-only: pending completions have NULL tokens and contribute nothing to sums, so event sums equal completion sums exactly. Idempotent and resumable.
Goes through the same sync path as live creation, so host hooks (Raif.config.inference_cost_event_metadata) fire for backfilled events too.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 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 149 150 151 152 153 154 155 156 157 |
# File 'app/models/raif/inference_cost_event.rb', line 98 def self.backfill!(batch_size: 500) failed_model_completion_ids = [] Raif::ModelCompletion .left_joins(:raif_inference_cost_event) .where("raif_inference_cost_events.id IS NULL OR raif_inference_cost_events.updated_at < raif_model_completions.updated_at") .where("completed_at IS NOT NULL OR failed_at IS NOT NULL") .in_batches(of: batch_size) do |batch| completions = begin batch.includes(:source).to_a rescue ActiveRecord::SubclassNotFound, NameError # A source row whose STI type no longer exists in the host app # (SubclassNotFound), or a source_type whose class was removed # entirely (NameError), makes the whole polymorphic preload raise. # Fall back to lazy per-record source loads for this batch; each # record's sync handles its own source resolution failure. batch.to_a end # enqueue_repair_on_failure: false because this IS the repair path; a # persistently failing record must not enqueue another full run per # failure (failures still report via Rails.error). completions.each do |model_completion| synced = model_completion.send(:sync_inference_cost_event, enqueue_repair_on_failure: false) event = model_completion.raif_inference_cost_event if synced && event&.persisted? begin # A stale-but-accurate event gets no timestamp bump from the # sync's save! (no attribute changed), so certify its freshness # explicitly: the archive job's eligibility guard reads # event.updated_at >= completion.updated_at. Only after a # successful sync - certifying an event whose re-sync failed # would mark stale spend data safe to cull. event.touch if event.updated_at < model_completion.updated_at rescue StandardError => e # Certification is part of the per-record repair operation: a # transient touch failure must accumulate like a sync failure # (raising the retryable aggregate error below) rather than # escape raw and abort the rest of the batch. Rails.error.report(e, handled: true, severity: :error) failed_model_completion_ids << model_completion.id end else failed_model_completion_ids << model_completion.id end end end return if failed_model_completion_ids.empty? # Per-record failures were already reported via Rails.error; raising here # makes the run itself fail so callers see the partial failure - the rake # task exits non-zero and Raif::RepairInferenceCostEventsJob gets bounded # retries from its queue backend instead of reporting success with events # still missing. raise Raif::Errors::InferenceCostEventsBackfillError, "Failed to create or re-sync inference cost events for #{failed_model_completion_ids.size} model completion(s) " \ "(ids: #{failed_model_completion_ids.first(20).join(", ")}#{failed_model_completion_ids.size > 20 ? ", ..." : ""})" end |
Instance Method Details
#model_completion_live? ⇒ Boolean
Column check, not a query: the FK is nullified at the DB level the moment the completion row is deleted.
161 162 163 |
# File 'app/models/raif/inference_cost_event.rb', line 161 def model_completion_live? raif_model_completion_id.present? end |