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, strict: false) ⇒ Response

Returns a new instance of Response.



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

def initialize(raw_response, response_format: nil, forced_extraction: false, strict: false)
  @raw_response = raw_response.is_a?(Hash) ? raw_response.with_indifferent_access : {}
  @response_format = response_format
  @forced_extraction = forced_extraction
  @strict = strict
  @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

#strictObject (readonly)

Returns the value of attribute strict.



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

def strict
  @strict
end

Instance Method Details

#[](key) ⇒ Object

Delegate common hash methods to raw_response for backward compatibility



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

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

#cached_tokensObject

Cached tokens (tokens served from cache)



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

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

#choicesObject



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

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

#completion_tokensObject

Total completion tokens



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

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

#contentObject

Content accessors



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

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



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

def cost_estimate
  return nil unless id && client

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

#createdObject



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

def created
  @raw_response["created"]
end

#dig(*keys) ⇒ Object



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

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

#error?Boolean

Convenience method to check if response indicates an error

Returns:

  • (Boolean)


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

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

#error_messageObject



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

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

#fetch(key, default = nil) ⇒ Object



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

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

#finish_reasonObject

Finish reason (standard OpenRouter format)



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

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

#has_content?Boolean

Convenience method to check if response has content

Returns:

  • (Boolean)


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

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

#has_key?(key) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#has_tool_calls?Boolean

Returns:

  • (Boolean)


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

def has_tool_calls?
  !tool_calls.empty?
end

#idObject



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

def id
  @raw_response["id"]
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#keysObject



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

def keys
  @raw_response.keys
end

#modelObject



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

def model
  @raw_response["model"]
end

#native_finish_reasonObject

Native finish reason from the provider



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

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

#objectObject



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

def object
  @raw_response["object"]
end

#prompt_tokensObject

Total prompt tokens



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

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

#providerObject

Provider information



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

def provider
  @raw_response["provider"]
end

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

Structured output methods

Raises:

  • (ArgumentError)


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

def structured_output(mode: nil, auto_heal: nil)
  mode ||= default_structured_output_mode
  # 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)

    # In the json_object path (lenient extraction) a parse failure yields nil rather
    # than raising. Strict mode must surface that as an error rather than returning nil.
    raise StructuredOutputError, "Failed to parse structured output from response" if result.nil?

    # 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)



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

def system_fingerprint
  @raw_response["system_fingerprint"]
end

#to_hObject



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

def to_h
  @raw_response.to_h
end

#to_json(*args) ⇒ Object



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

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

#to_messageObject

Convert response to message format for conversation continuation



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

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



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

def tool_calls
  @tool_calls ||= parse_tool_calls
end

#total_tokensObject

Total tokens (prompt + completion)



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

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

#usageObject



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

def usage
  @raw_response["usage"]
end

#valid_structured_output?Boolean

Returns:

  • (Boolean)


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

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



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

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