Class: Observ::ImageGenerationInstrumenter

Inherits:
Object
  • Object
show all
Defined in:
app/services/observ/image_generation_instrumenter.rb

Constant Summary collapse

IMAGE_PRICING =

Hardcoded pricing for image generation models (USD per image) Prices are organized by model_id, then by size, then by quality Source: https://openai.com/pricing, https://cloud.google.com/vertex-ai/pricing

{
  # OpenAI DALL-E 3 (size and quality based)
  # Quality options: "standard", "hd"
  "dall-e-3" => {
    "1024x1024" => { "standard" => 0.04, "hd" => 0.08 },
    "1792x1024" => { "standard" => 0.08, "hd" => 0.12 },
    "1024x1792" => { "standard" => 0.08, "hd" => 0.12 }
  },
  # OpenAI DALL-E 2 (size based, no quality option)
  "dall-e-2" => {
    "1024x1024" => { "default" => 0.02 },
    "512x512" => { "default" => 0.018 },
    "256x256" => { "default" => 0.016 }
  },
  # OpenAI GPT-image-1 (token-based, estimated per-image costs)
  # Quality options: "low", "medium", "high" (maps "standard" -> "medium")
  # Source: "Image outputs cost approximately $0.01 (low), $0.04 (medium), $0.17 (high) for square images"
  # Larger sizes are estimated at ~1.7x for 1792x1024 and ~2.9x for 1792x1792
  "gpt-image-1" => {
    "1024x1024" => { "low" => 0.01, "medium" => 0.04, "high" => 0.17 },
    "1792x1024" => { "low" => 0.017, "medium" => 0.068, "high" => 0.29 },
    "1024x1792" => { "low" => 0.017, "medium" => 0.068, "high" => 0.29 },
    "1792x1792" => { "low" => 0.029, "medium" => 0.116, "high" => 0.49 },
    "default" => { "low" => 0.01, "medium" => 0.04, "high" => 0.17 }
  },
  # OpenAI GPT-image-1-mini (token-based, estimated per-image costs)
  # Approximately 5x cheaper than gpt-image-1 based on token pricing ratio
  "gpt-image-1-mini" => {
    "1024x1024" => { "low" => 0.002, "medium" => 0.008, "high" => 0.034 },
    "1792x1024" => { "low" => 0.0034, "medium" => 0.0136, "high" => 0.058 },
    "1024x1792" => { "low" => 0.0034, "medium" => 0.0136, "high" => 0.058 },
    "1792x1792" => { "low" => 0.0058, "medium" => 0.0232, "high" => 0.098 },
    "default" => { "low" => 0.002, "medium" => 0.008, "high" => 0.034 }
  },
  # Google Imagen models (flat rate per image)
  "imagen-3.0-generate-002" => {
    "default" => { "default" => 0.04 }
  },
  "imagen-4.0-generate-001" => {
    "default" => { "default" => 0.04 }
  },
  "imagen-4.0-generate-preview-06-06" => {
    "default" => { "default" => 0.04 }
  },
  "imagen-4.0-ultra-generate-preview-06-06" => {
    "default" => { "default" => 0.08 }
  }
}.freeze
QUALITY_MAPPINGS =

Maps quality names between different conventions DALL-E uses: "standard", "hd" GPT-image uses: "low", "medium", "high"

{
  "standard" => "medium",  # Map DALL-E "standard" to GPT-image "medium"
  "hd" => "high"           # Map DALL-E "hd" to GPT-image "high"
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(session, context: {}) ⇒ ImageGenerationInstrumenter

Returns a new instance of ImageGenerationInstrumenter.



67
68
69
70
71
72
# File 'app/services/observ/image_generation_instrumenter.rb', line 67

def initialize(session, context: {})
  @session = session
  @context = context
  @original_paint_method = nil
  @instrumented = false
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



65
66
67
# File 'app/services/observ/image_generation_instrumenter.rb', line 65

def context
  @context
end

#sessionObject (readonly)

Returns the value of attribute session.



65
66
67
# File 'app/services/observ/image_generation_instrumenter.rb', line 65

def session
  @session
end

Instance Method Details

#instrument!Object



74
75
76
77
78
79
80
81
# File 'app/services/observ/image_generation_instrumenter.rb', line 74

def instrument!
  return if @instrumented

  wrap_paint_method
  @instrumented = true

  Rails.logger.info "[Observability] Instrumented RubyLLM.paint for session #{session.session_id}"
end

#uninstrument!Object



83
84
85
86
87
88
89
90
91
# File 'app/services/observ/image_generation_instrumenter.rb', line 83

def uninstrument!
  return unless @instrumented
  return unless @original_paint_method

  RubyLLM.define_singleton_method(:paint, @original_paint_method)
  @instrumented = false

  Rails.logger.info "[Observability] Uninstrumented RubyLLM.paint"
end