Class: OpenRouter::Response

Inherits:
Object
  • Object
show all
Defined in:
lib/open_router/response.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(raw_response, response_format: nil, forced_extraction: false) ⇒ Response

Returns a new instance of Response.



13
14
15
16
17
18
# File 'lib/open_router/response.rb', line 13

def initialize(raw_response, response_format: nil, forced_extraction: false)
  @raw_response = raw_response.is_a?(Hash) ? raw_response.with_indifferent_access : {}
  @response_format = response_format
  @forced_extraction = forced_extraction
  @client = nil
end

Instance Attribute Details

#clientObject

Returns the value of attribute client.



11
12
13
# File 'lib/open_router/response.rb', line 11

def client
  @client
end

#forced_extractionObject (readonly)

Returns the value of attribute forced_extraction.



10
11
12
# File 'lib/open_router/response.rb', line 10

def forced_extraction
  @forced_extraction
end

#raw_responseObject (readonly)

Returns the value of attribute raw_response.



10
11
12
# File 'lib/open_router/response.rb', line 10

def raw_response
  @raw_response
end

#response_formatObject (readonly)

Returns the value of attribute response_format.



10
11
12
# File 'lib/open_router/response.rb', line 10

def response_format
  @response_format
end

Instance Method Details

#[](key) ⇒ Object

Delegate common hash methods to raw_response for backward compatibility



21
22
23
# File 'lib/open_router/response.rb', line 21

def [](key)
  @raw_response[key]
end

#cached_tokensObject

Cached tokens (tokens served from cache)



214
215
216
# File 'lib/open_router/response.rb', line 214

def cached_tokens
  usage&.dig("prompt_tokens_details", "cached_tokens") || 0
end

#choicesObject



169
170
171
# File 'lib/open_router/response.rb', line 169

def choices
  @raw_response["choices"] || []
end

#completion_tokensObject

Total completion tokens



224
225
226
# File 'lib/open_router/response.rb', line 224

def completion_tokens
  usage&.dig("completion_tokens") || 0
end

#contentObject

Content accessors



165
166
167
# File 'lib/open_router/response.rb', line 165

def content
  choices.first&.dig("message", "content")
end

#cost_estimateObject

Get estimated cost for this response Note: This requires an additional API call to /generation endpoint



235
236
237
238
239
240
241
# File 'lib/open_router/response.rb', line 235

def cost_estimate
  return nil unless id && client

  @cost_estimate ||= client.query_generation_stats(id)&.dig("cost")
rescue StandardError
  nil
end

#createdObject



185
186
187
# File 'lib/open_router/response.rb', line 185

def created
  @raw_response["created"]
end

#dig(*keys) ⇒ Object



25
26
27
# File 'lib/open_router/response.rb', line 25

def dig(*keys)
  @raw_response.dig(*keys)
end

#error?Boolean

Convenience method to check if response indicates an error

Returns:

  • (Boolean)


249
250
251
# File 'lib/open_router/response.rb', line 249

def error?
  @raw_response.key?("error")
end

#error_messageObject



253
254
255
# File 'lib/open_router/response.rb', line 253

def error_message
  @raw_response.dig("error", "message")
end

#fetch(key, default = nil) ⇒ Object



29
30
31
# File 'lib/open_router/response.rb', line 29

def fetch(key, default = nil)
  @raw_response.fetch(key, default)
end

#finish_reasonObject

Finish reason (standard OpenRouter format)



209
210
211
# File 'lib/open_router/response.rb', line 209

def finish_reason
  choices.first&.dig("finish_reason")
end

#has_content?Boolean

Convenience method to check if response has content

Returns:

  • (Boolean)


244
245
246
# File 'lib/open_router/response.rb', line 244

def has_content?
  !content.nil? && !content.empty?
end

#has_key?(key) ⇒ Boolean

Returns:

  • (Boolean)


41
42
43
# File 'lib/open_router/response.rb', line 41

def has_key?(key)
  @raw_response.key?(key)
end

#has_tool_calls?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/open_router/response.rb', line 58

def has_tool_calls?
  !tool_calls.empty?
end

#idObject



177
178
179
# File 'lib/open_router/response.rb', line 177

def id
  @raw_response["id"]
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/open_router/response.rb', line 33

def key?(key)
  @raw_response.key?(key)
end

#keysObject



37
38
39
# File 'lib/open_router/response.rb', line 37

def keys
  @raw_response.keys
end

#modelObject



181
182
183
# File 'lib/open_router/response.rb', line 181

def model
  @raw_response["model"]
end

#native_finish_reasonObject

Native finish reason from the provider



204
205
206
# File 'lib/open_router/response.rb', line 204

def native_finish_reason
  choices.first&.dig("native_finish_reason")
end

#objectObject



189
190
191
# File 'lib/open_router/response.rb', line 189

def object
  @raw_response["object"]
end

#prompt_tokensObject

Total prompt tokens



219
220
221
# File 'lib/open_router/response.rb', line 219

def prompt_tokens
  usage&.dig("prompt_tokens") || 0
end

#providerObject

Provider information



194
195
196
# File 'lib/open_router/response.rb', line 194

def provider
  @raw_response["provider"]
end

#structured_output(mode: nil, auto_heal: nil) ⇒ Object

Structured output methods

Raises:

  • (ArgumentError)


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
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
# File 'lib/open_router/response.rb', line 79

def structured_output(mode: nil, auto_heal: nil)
  # Use global default mode if not specified
  if mode.nil?
    mode = if @client&.configuration.respond_to?(:default_structured_output_mode)
             @client.configuration.default_structured_output_mode || :strict
           else
             :strict
           end
  end
  # Validate mode parameter
  raise ArgumentError, "Invalid mode: #{mode}. Must be :strict or :gentle." unless %i[strict gentle].include?(mode)

  return nil unless structured_output_expected? && has_content?

  case mode
  when :strict
    # The existing logic for strict parsing and healing
    should_heal = if auto_heal.nil?
                    @client&.configuration&.auto_heal_responses
                  else
                    auto_heal
                  end

    result = parse_and_heal_structured_output(auto_heal: should_heal)

    # Only validate after parsing if healing is disabled (healing handles its own validation)
    if result && !should_heal
      schema_obj = extract_schema_from_response_format
      if schema_obj && !schema_obj.validate(result)
        validation_errors = schema_obj.validation_errors(result)
        raise StructuredOutputError, "Schema validation failed: #{validation_errors.join(", ")}"
      end
    end

    # Use a flag rather than ||= so nil results don't trigger re-parsing on every call
    unless @structured_output_computed
      @structured_output = result
      @structured_output_computed = true
    end
    @structured_output
  when :gentle
    # New gentle mode: best-effort parsing, no healing, no validation
    content_to_parse = @forced_extraction ? extract_json_from_text(content) : content
    return nil if content_to_parse.nil?

    begin
      JSON.parse(content_to_parse)
    rescue JSON::ParserError
      nil # Return nil on failure instead of raising an error
    end
  end
end

#system_fingerprintObject

System fingerprint (model version identifier)



199
200
201
# File 'lib/open_router/response.rb', line 199

def system_fingerprint
  @raw_response["system_fingerprint"]
end

#to_hObject



45
46
47
# File 'lib/open_router/response.rb', line 45

def to_h
  @raw_response.to_h
end

#to_json(*args) ⇒ Object



49
50
51
# File 'lib/open_router/response.rb', line 49

def to_json(*args)
  @raw_response.to_json(*args)
end

#to_messageObject

Convert response to message format for conversation continuation



63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/open_router/response.rb', line 63

def to_message
  if has_tool_calls?
    {
      role: "assistant",
      content: content,
      tool_calls: raw_tool_calls
    }
  else
    {
      role: "assistant",
      content: content
    }
  end
end

#tool_callsObject

Tool calling methods



54
55
56
# File 'lib/open_router/response.rb', line 54

def tool_calls
  @tool_calls ||= parse_tool_calls
end

#total_tokensObject

Total tokens (prompt + completion)



229
230
231
# File 'lib/open_router/response.rb', line 229

def total_tokens
  usage&.dig("total_tokens") || 0
end

#usageObject



173
174
175
# File 'lib/open_router/response.rb', line 173

def usage
  @raw_response["usage"]
end

#valid_structured_output?Boolean

Returns:

  • (Boolean)


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/open_router/response.rb', line 132

def valid_structured_output?
  return true unless structured_output_expected?

  schema_obj = extract_schema_from_response_format
  return true unless schema_obj

  begin
    parsed_output = structured_output
    return false unless parsed_output

    schema_obj.validate(parsed_output)
  rescue StructuredOutputError
    false
  end
end

#validation_errorsObject



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/open_router/response.rb', line 148

def validation_errors
  return [] unless structured_output_expected?

  schema_obj = extract_schema_from_response_format
  return [] unless schema_obj

  begin
    parsed_output = structured_output
    return [] unless parsed_output

    schema_obj.validation_errors(parsed_output)
  rescue StructuredOutputError
    ["Failed to parse structured output"]
  end
end