Class: OpenRouter::Response

Inherits:
Object
  • Object
show all
Includes:
ResponseParsing
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.



16
17
18
19
20
21
# File 'lib/open_router/response.rb', line 16

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.



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

def client
  @client
end

#forced_extractionObject (readonly)

Returns the value of attribute forced_extraction.



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

def forced_extraction
  @forced_extraction
end

#raw_responseObject (readonly)

Returns the value of attribute raw_response.



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

def raw_response
  @raw_response
end

#response_formatObject (readonly)

Returns the value of attribute response_format.



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

def response_format
  @response_format
end

Instance Method Details

#[](key) ⇒ Object

Delegate common hash methods to raw_response for backward compatibility



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

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

#cached_tokensObject

Cached tokens (tokens served from cache)



221
222
223
# File 'lib/open_router/response.rb', line 221

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

#choicesObject



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

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

#completion_tokensObject

Total completion tokens



231
232
233
# File 'lib/open_router/response.rb', line 231

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

#contentObject

Content accessors



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

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



242
243
244
245
246
247
248
# File 'lib/open_router/response.rb', line 242

def cost_estimate
  return nil unless id && client

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

#createdObject



192
193
194
# File 'lib/open_router/response.rb', line 192

def created
  @raw_response["created"]
end

#dig(*keys) ⇒ Object



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

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

#error?Boolean

Convenience method to check if response indicates an error

Returns:

  • (Boolean)


256
257
258
# File 'lib/open_router/response.rb', line 256

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

#error_messageObject



260
261
262
# File 'lib/open_router/response.rb', line 260

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

#fetch(key, default = nil) ⇒ Object



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

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

#finish_reasonObject

Finish reason (standard OpenRouter format)



216
217
218
# File 'lib/open_router/response.rb', line 216

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

#has_content?Boolean

Convenience method to check if response has content

Returns:

  • (Boolean)


251
252
253
# File 'lib/open_router/response.rb', line 251

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

#has_key?(key) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#has_tool_calls?Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/open_router/response.rb', line 61

def has_tool_calls?
  !tool_calls.empty?
end

#idObject



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

def id
  @raw_response["id"]
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#keysObject



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

def keys
  @raw_response.keys
end

#modelObject Also known as: selected_model



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

def model
  @raw_response["model"]
end

#native_finish_reasonObject

Native finish reason from the provider



211
212
213
# File 'lib/open_router/response.rb', line 211

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

#objectObject



196
197
198
# File 'lib/open_router/response.rb', line 196

def object
  @raw_response["object"]
end

#prompt_tokensObject

Total prompt tokens



226
227
228
# File 'lib/open_router/response.rb', line 226

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

#providerObject

Provider information



201
202
203
# File 'lib/open_router/response.rb', line 201

def provider
  @raw_response["provider"]
end

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

Structured output methods

Raises:

  • (ArgumentError)


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
131
132
133
# File 'lib/open_router/response.rb', line 82

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)



206
207
208
# File 'lib/open_router/response.rb', line 206

def system_fingerprint
  @raw_response["system_fingerprint"]
end

#to_hObject



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

def to_h
  @raw_response.to_h
end

#to_json(*args) ⇒ Object



52
53
54
# File 'lib/open_router/response.rb', line 52

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

#to_messageObject

Convert response to message format for conversation continuation



66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/open_router/response.rb', line 66

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



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

def tool_calls
  @tool_calls ||= parse_tool_calls
end

#total_tokensObject

Total tokens (prompt + completion)



236
237
238
# File 'lib/open_router/response.rb', line 236

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

#usageObject



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

def usage
  @raw_response["usage"]
end

#valid_structured_output?Boolean

Returns:

  • (Boolean)


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/open_router/response.rb', line 135

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



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/open_router/response.rb', line 151

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