Module: DSPy::LM::UsageFactory

Extended by:
T::Sig
Defined in:
lib/dspy/lm/usage.rb

Overview

Factory for creating appropriate usage objects

Class Method Summary collapse

Class Method Details

.convert_to_hash(value) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/dspy/lm/usage.rb', line 131

def self.convert_to_hash(value)
  return nil if value.nil?
  return value if value.is_a?(Hash) && value.keys.all? { |k| k.is_a?(Symbol) }
  
  # Convert object to hash if it responds to to_h
  if value.respond_to?(:to_h)
    hash = value.to_h
    # Ensure symbol keys and integer values
    hash.transform_keys(&:to_sym).transform_values(&:to_i)
  else
    nil
  end
rescue
  nil
end

.create(provider, usage_data) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/dspy/lm/usage.rb', line 76

def self.create(provider, usage_data)
  return nil if usage_data.nil?
  
  # If already a Usage struct, return as-is
  return usage_data if usage_data.is_a?(Usage)
  
  # Handle test doubles by converting to hash
  if usage_data.respond_to?(:to_h)
    usage_data = usage_data.to_h
  end
  
  # Convert hash to appropriate struct
  return nil unless usage_data.is_a?(Hash)
  
  # Normalize keys to symbols
  normalized = usage_data.transform_keys(&:to_sym)
  
  case provider.to_s.downcase
  when 'openai'
    create_openai_usage(normalized)
  when 'anthropic'
    create_anthropic_usage(normalized)
  when 'gemini'
    create_gemini_usage(normalized)
  else
    create_generic_usage(normalized)
  end
end

.create_anthropic_usage(data) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/dspy/lm/usage.rb', line 148

def self.create_anthropic_usage(data)
  # Anthropic uses input_tokens/output_tokens
  input_tokens = data[:input_tokens] || 0
  output_tokens = data[:output_tokens] || 0
  total_tokens = data[:total_tokens] || (input_tokens + output_tokens)

  AnthropicUsage.new(
    input_tokens: input_tokens,
    output_tokens: output_tokens,
    total_tokens: total_tokens,
    cache_creation_input_tokens: data[:cache_creation_input_tokens],
    cache_read_input_tokens: data[:cache_read_input_tokens]
  )
rescue StandardError => e
  DSPy.logger.debug("Failed to create Anthropic usage: #{e.message}")
  nil
end

.create_gemini_usage(data) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/dspy/lm/usage.rb', line 167

def self.create_gemini_usage(data)
  # Gemini uses promptTokenCount/candidatesTokenCount/totalTokenCount
  input_tokens = data[:promptTokenCount] || data[:input_tokens] || 0
  output_tokens = data[:candidatesTokenCount] || data[:output_tokens] || 0
  total_tokens = data[:totalTokenCount] || data[:total_tokens] || (input_tokens + output_tokens)
  
  Usage.new(
    input_tokens: input_tokens,
    output_tokens: output_tokens,
    total_tokens: total_tokens
  )
rescue StandardError => e
  DSPy.logger.debug("Failed to create Gemini usage: #{e.message}")
  nil
end

.create_generic_usage(data) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/dspy/lm/usage.rb', line 184

def self.create_generic_usage(data)
  # Generic fallback
  input_tokens = data[:input_tokens] || data[:prompt_tokens] || 0
  output_tokens = data[:output_tokens] || data[:completion_tokens] || 0
  total_tokens = data[:total_tokens] || (input_tokens + output_tokens)
  
  Usage.new(
    input_tokens: input_tokens,
    output_tokens: output_tokens,
    total_tokens: total_tokens
  )
rescue StandardError => e
  DSPy.logger.debug("Failed to create generic usage: #{e.message}")
  nil
end

.create_openai_usage(data) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/dspy/lm/usage.rb', line 108

def self.create_openai_usage(data)
  # OpenAI uses prompt_tokens/completion_tokens
  input_tokens = data[:prompt_tokens] || data[:input_tokens] || 0
  output_tokens = data[:completion_tokens] || data[:output_tokens] || 0
  total_tokens = data[:total_tokens] || (input_tokens + output_tokens)
  
  # Convert prompt_tokens_details and completion_tokens_details to hashes if needed
  prompt_details = convert_to_hash(data[:prompt_tokens_details])
  completion_details = convert_to_hash(data[:completion_tokens_details])
  
  OpenAIUsage.new(
    input_tokens: input_tokens,
    output_tokens: output_tokens,
    total_tokens: total_tokens,
    prompt_tokens_details: prompt_details,
    completion_tokens_details: completion_details
  )
rescue StandardError => e
  DSPy.logger.debug("Failed to create OpenAI usage: #{e.message}")
  nil
end