Class: Tracebook::ExportJob
- Inherits:
-
ApplicationJob
- Object
- ActiveJob::Base
- ApplicationJob
- Tracebook::ExportJob
- Defined in:
- app/jobs/tracebook/export_job.rb
Overview
Background job for exporting interactions to CSV or NDJSON.
Streams filtered interactions to an export file stored in ActiveStorage. Supports filtering by provider, model, project, date range, tags, etc.
Supported Formats
- CSV - Comma-separated values with headers
- NDJSON - Newline-delimited JSON (one interaction per line)
Exported Fields
- timestamp, project, provider, model, status
- input_tokens, output_tokens, cost_total_cents
- tags (pipe-separated)
- request_payload, response_payload (JSON)
- metadata
Instance Method Summary collapse
- #content_type_for(format) ⇒ Object private
- #create_blob(data, format) ⇒ Object private
- #csv_for(interactions) ⇒ Object private
- #csv_headers ⇒ Object private
- #export_as(interactions, format) ⇒ Object private
- #load_payload(interaction, type) ⇒ Object private
- #ndjson_for(interactions) ⇒ Object private
-
#perform(format:, filters: {}) ⇒ ActiveStorage::Blob
Exports filtered interactions to specified format.
- #serialize_interaction(interaction) ⇒ Object private
Instance Method Details
#content_type_for(format) ⇒ Object (private)
132 133 134 135 136 137 138 139 140 141 |
# File 'app/jobs/tracebook/export_job.rb', line 132 def content_type_for(format) case format.to_sym when :csv "text/csv" when :ndjson "application/x-ndjson" else "application/octet-stream" end end |
#create_blob(data, format) ⇒ Object (private)
124 125 126 127 128 129 130 |
# File 'app/jobs/tracebook/export_job.rb', line 124 def create_blob(data, format) ActiveStorage::Blob.create_and_upload!( io: StringIO.new(data), filename: "tracebook-export-#{Time.current.to_i}.#{format}", content_type: content_type_for(format) ) end |
#csv_for(interactions) ⇒ Object (private)
77 78 79 80 81 82 83 84 |
# File 'app/jobs/tracebook/export_job.rb', line 77 def csv_for(interactions) CSV.generate do |csv| csv << csv_headers interactions.find_each do |interaction| csv << serialize_interaction(interaction).values_at(*csv_headers) end end end |
#csv_headers ⇒ Object (private)
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'app/jobs/tracebook/export_job.rb', line 143 def csv_headers @csv_headers ||= [ "timestamp", "project", "provider", "model", "status", "input_tokens", "output_tokens", "cost_total_cents", "tags", "request_payload", "response_payload", "metadata" ] end |
#export_as(interactions, format) ⇒ Object (private)
66 67 68 69 70 71 72 73 74 75 |
# File 'app/jobs/tracebook/export_job.rb', line 66 def export_as(interactions, format) case format when :csv csv_for(interactions) when :ndjson ndjson_for(interactions) else raise ArgumentError, "Unsupported export format: #{format}" end end |
#load_payload(interaction, type) ⇒ Object (private)
112 113 114 115 116 117 118 119 120 121 122 |
# File 'app/jobs/tracebook/export_job.rb', line 112 def load_payload(interaction, type) store = interaction.public_send("#{type}_payload_store") if store == "active_storage" blob = interaction.public_send("#{type}_payload_blob") return JSON.parse(blob.download) if blob end interaction.public_send("#{type}_payload") rescue JSON::ParserError interaction.public_send("#{type}_payload") end |
#ndjson_for(interactions) ⇒ Object (private)
86 87 88 89 90 91 92 |
# File 'app/jobs/tracebook/export_job.rb', line 86 def ndjson_for(interactions) Enumerator.new do |yielder| interactions.find_each do |interaction| yielder << serialize_interaction(interaction).to_json end end.to_a.join("\n") end |
#perform(format:, filters: {}) ⇒ ActiveStorage::Blob
Exports filtered interactions to specified format.
58 59 60 61 62 |
# File 'app/jobs/tracebook/export_job.rb', line 58 def perform(format:, filters: {}) interactions = Interaction.filtered(filters).order(:created_at) data = export_as(interactions, format.to_sym) create_blob(data, format) end |
#serialize_interaction(interaction) ⇒ Object (private)
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'app/jobs/tracebook/export_job.rb', line 94 def serialize_interaction(interaction) payload = { "timestamp" => interaction.created_at.iso8601, "project" => interaction.project, "provider" => interaction.provider, "model" => interaction.model, "status" => interaction.status, "input_tokens" => interaction.input_tokens, "output_tokens" => interaction.output_tokens, "cost_total_cents" => interaction.cost_total_cents, "tags" => Array(interaction.).join("|"), "request_payload" => load_payload(interaction, :request), "response_payload" => load_payload(interaction, :response) } payload.merge("metadata" => interaction.) end |