Class: OpenRouter::Client

Inherits:
Object
  • Object
show all
Includes:
Callbacks, HTTP, ParameterBuilder, RequestHandler, Routing, ToolSerializer
Defined in:
lib/open_router/client.rb,
sig/open_router.rbs

Direct Known Subclasses

StreamingClient

Constant Summary

Constants included from Routing

Routing::FUSION_MODEL, Routing::PARETO_CODE_MODEL

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Routing

#fuse, #pareto_complete

Methods included from Callbacks

#clear_callbacks, #on, #trigger_callbacks

Methods included from HTTP

#delete, #get, #multipart_post, #post

Constructor Details

#initialize(access_token: nil, request_timeout: nil, uri_base: nil, extra_headers: {}, track_usage: true) {|@configuration| ... } ⇒ Client

Returns a new instance of Client.

Yields:

Yield Parameters:

Yield Returns:

  • (void)


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/open_router/client.rb', line 26

def initialize(access_token: nil, request_timeout: nil, uri_base: nil, extra_headers: {}, track_usage: true)
  # Build a per-instance configuration to avoid mutating the global singleton,
  # which would cause credential leakage across Client instances in concurrent use.
  @configuration = OpenRouter.configuration.dup
  @configuration.extra_headers = OpenRouter.configuration.extra_headers.dup
  @configuration.access_token = access_token if access_token
  @configuration.request_timeout = request_timeout if request_timeout
  @configuration.uri_base = uri_base if uri_base
  @configuration.extra_headers = @configuration.extra_headers.merge(extra_headers) if extra_headers.any?
  yield(@configuration) if block_given?

  @capability_warnings_shown = Set.new

  @callbacks = {
    before_request: [],
    after_response: [],
    on_tool_call: [],
    on_error: [],
    on_stream_chunk: [],
    on_healing: []
  }

  @track_usage = track_usage
  @usage_tracker = UsageTracker.new if @track_usage
end

Instance Attribute Details

#callbacksObject (readonly)

Returns the value of attribute callbacks.



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

def callbacks
  @callbacks
end

#configurationObject (readonly)

Returns the value of attribute configuration.



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

def configuration
  @configuration
end

#usage_trackerObject (readonly)

Returns the value of attribute usage_tracker.



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

def usage_tracker
  @usage_tracker
end

Instance Method Details

#complete(messages, options = nil, stream: nil, **kwargs) ⇒ Response

Performs a chat completion request to the OpenRouter API.

Parameters:

  • messages (Array<Hash>)

    Array of message hashes with role and content

  • options (CompletionOptions, Hash, nil) (defaults to: nil)

    Options object or hash with configuration

  • stream (Proc, nil) (defaults to: nil)

    Optional callable object for streaming

  • kwargs (Hash)

    Additional options (merged with options parameter)

Returns:

  • (Response)

    The completion response wrapped in a Response object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/open_router/client.rb', line 59

def complete(messages, options = nil, stream: nil, **kwargs)
  opts = normalize_options(options, kwargs)
  parameters = prepare_base_parameters(messages, opts, stream)
  forced_extraction = configure_tools_and_structured_outputs!(parameters, opts)
  configure_plugins!(parameters, opts.response_format, stream)
  validate_vision_support(opts.model, messages)

  trigger_callbacks(:before_request, parameters)

  raw_response = execute_request(parameters)
  validate_response!(raw_response, stream)

  response = build_response(raw_response, opts.response_format, forced_extraction)

  model_for_tracking = opts.model.is_a?(String) ? opts.model : opts.model.first
  @usage_tracker&.track(response, model: model_for_tracking)

  trigger_callbacks(:after_response, response)
  trigger_callbacks(:on_tool_call, response.tool_calls) if response.has_tool_calls?

  response
end

#modelsArray[Hash[String, untyped]]

Fetches the list of available models from the OpenRouter API.

Returns:

  • (Array[Hash[String, untyped]])


83
84
85
# File 'lib/open_router/client.rb', line 83

def models
  get(path: "/models")["data"]
end

#query_generation_stats(generation_id) ⇒ Hash[String, untyped]

Queries the generation stats for a given id.

Parameters:

  • generation_id: (String)

Returns:

  • (Hash[String, untyped])


88
89
90
91
# File 'lib/open_router/client.rb', line 88

def query_generation_stats(generation_id)
  response = get(path: "/generation?id=#{generation_id}")
  response["data"]
end

#responses(input, options = nil, **kwargs) ⇒ ResponsesResponse

Performs a request to the Responses API Beta (/api/v1/responses)

Parameters:

  • input (String, Array)

    The input text or structured message array

  • options (CompletionOptions, Hash, nil) (defaults to: nil)

    Options object or hash with configuration

  • kwargs (Hash)

    Additional options (merged with options parameter)

Returns:



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/open_router/client.rb', line 99

def responses(input, options = nil, **kwargs)
  opts = normalize_options(options, kwargs)

  if opts.model == "openrouter/auto"
    raise ArgumentError, "model is required for responses API (cannot use default 'openrouter/auto')"
  end

  parameters = { model: opts.model, input: input }
  parameters[:reasoning] = opts.reasoning if opts.reasoning
  parameters[:tools] = serialize_tools_for_responses(opts.tools) if opts.tools?
  parameters[:tool_choice] = opts.tool_choice if opts.tool_choice
  parameters[:max_output_tokens] = opts.max_completion_tokens || opts.max_tokens if opts.max_completion_tokens || opts.max_tokens
  parameters[:temperature] = opts.temperature if opts.temperature
  parameters[:top_p] = opts.top_p if opts.top_p
  parameters.merge!(opts.extras || {})

  raw = post(path: "/responses", parameters: parameters)
  ResponsesResponse.new(raw)
end

#select_modelObject

Create a new ModelSelector for intelligent model selection



120
121
122
# File 'lib/open_router/client.rb', line 120

def select_model
  ModelSelector.new
end

#smart_complete(messages, requirements: {}, optimization: :cost, **extras) ⇒ Object

Smart completion that automatically selects the best model based on requirements



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/open_router/client.rb', line 125

def smart_complete(messages, requirements: {}, optimization: :cost, **extras)
  selector = ModelSelector.new.optimize_for(optimization)

  selector = selector.require(*requirements[:capabilities]) if requirements[:capabilities]

  if requirements[:max_cost] || requirements[:max_input_cost]
    cost_opts = {}
    cost_opts[:max_cost] = requirements[:max_cost] || requirements[:max_input_cost]
    cost_opts[:max_output_cost] = requirements[:max_output_cost] if requirements[:max_output_cost]
    selector = selector.within_budget(**cost_opts)
  end

  selector = selector.min_context(requirements[:min_context_length]) if requirements[:min_context_length]

  if requirements[:providers]
    case requirements[:providers]
    when Hash
      selector = selector.prefer_providers(*requirements[:providers][:prefer]) if requirements[:providers][:prefer]
      selector = selector.require_providers(*requirements[:providers][:require]) if requirements[:providers][:require]
      selector = selector.avoid_providers(*requirements[:providers][:avoid]) if requirements[:providers][:avoid]
    when Array
      selector = selector.prefer_providers(*requirements[:providers])
    end
  end

  model = selector.choose
  raise ModelSelectionError, "No model found matching requirements: #{requirements}" unless model

  complete(messages, model:, **extras)
end

#smart_complete_with_fallback(messages, requirements: {}, optimization: :cost, max_retries: 3, **extras) ⇒ Object

Smart completion with automatic fallback to alternative models



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/open_router/client.rb', line 157

def smart_complete_with_fallback(messages, requirements: {}, optimization: :cost, max_retries: 3, **extras)
  selector = ModelSelector.new.optimize_for(optimization)

  selector = selector.require(*requirements[:capabilities]) if requirements[:capabilities]

  if requirements[:max_cost] || requirements[:max_input_cost]
    cost_opts = {}
    cost_opts[:max_cost] = requirements[:max_cost] || requirements[:max_input_cost]
    cost_opts[:max_output_cost] = requirements[:max_output_cost] if requirements[:max_output_cost]
    selector = selector.within_budget(**cost_opts)
  end

  selector = selector.min_context(requirements[:min_context_length]) if requirements[:min_context_length]

  if requirements[:providers]
    case requirements[:providers]
    when Hash
      selector = selector.prefer_providers(*requirements[:providers][:prefer]) if requirements[:providers][:prefer]
      selector = selector.require_providers(*requirements[:providers][:require]) if requirements[:providers][:require]
      selector = selector.avoid_providers(*requirements[:providers][:avoid]) if requirements[:providers][:avoid]
    when Array
      selector = selector.prefer_providers(*requirements[:providers])
    end
  end

  fallback_models = selector.choose_with_fallbacks(limit: max_retries + 1)
  raise ModelSelectionError, "No models found matching requirements: #{requirements}" if fallback_models.empty?

  last_error = nil

  fallback_models.each do |model|
    return complete(messages, model:, **extras)
  rescue StandardError => e
    last_error = e
  end

  raise ModelSelectionError, "All fallback models failed. Last error: #{last_error&.message}"
end