open_router_enhanced 1.2.5 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 006c0d9fe0bb456345df1e75db430cf54943d85f34c55a931cf3490aa7e8ad45
4
- data.tar.gz: 15953a9890fa76b4d039ff40357e11407bbd03fe44d731926327e65a909e7b94
3
+ metadata.gz: d443d948a07c5b55d6366e135354b2faa07a8edc38cb2791237a6a4a92bd229a
4
+ data.tar.gz: 37a93b36720b58bf1ee1c4809aa4d54e4d29e06ba27a63c9285a78fa7074eb66
5
5
  SHA512:
6
- metadata.gz: 7048aec5f2abd698c35300fea40135c149ea90345c4e9897d14ac1114ba88a85b1b5ddcef77b3dfc1f01993c5dcb1dd922e011a3fb0162dfcdf1ab1b518de933
7
- data.tar.gz: 859c1f7a15f59b11a530eb7eb460e36499782dda46eb76affdf74e63f474f9e40b56d24b4bea65fb6cb08b5438e0186f04b73aed99cf2dd6de2eeebc5a83691d
6
+ metadata.gz: 78fb6b74df5a7cb901ecac23fe503c667035e4794d6e7d9b97d4ad703e1f0a40347bd3274b86ba13c35501c8afc0b3c29ec5cb7b632eb93013af5a5328403285
7
+ data.tar.gz: 51d704a3035cf8211ac0d9533931d0b1a9bc9ebe49d21f192dd49b7dbb423eadcbf5adcc7b99d3212002947f2646337122e99660a589fa54be22ab3a356555eb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,254 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [2.0.0] - 2025-12-28
4
+
5
+ ### Overview
6
+
7
+ Version 2.0.0 introduces the `CompletionOptions` class - a structured, self-documenting way to configure API requests. This replaces the previous pattern of 11+ keyword arguments with a clean, reusable options object.
8
+
9
+ **This is a semver major release, but existing code will continue to work without modification.** The new patterns are opt-in and recommended for new code.
10
+
11
+ ---
12
+
13
+ ### Breaking Changes
14
+
15
+ Method signatures now accept an optional `CompletionOptions` object as the second parameter:
16
+
17
+ | Method | Old Signature | New Signature |
18
+ |--------|---------------|---------------|
19
+ | `complete` | `(messages, model:, tools:, ...)` | `(messages, options = nil, stream:, **kwargs)` |
20
+ | `stream_complete` | `(messages, model:, ...)` | `(messages, options = nil, accumulate_response:, **kwargs, &block)` |
21
+ | `stream` | `(messages, model:, ...)` | `(messages, options = nil, **kwargs, &block)` |
22
+ | `responses` | `(input, model:, ...)` | `(input, options = nil, **kwargs)` |
23
+
24
+ **Important**: The `options` parameter accepts `CompletionOptions`, `Hash`, or `nil`. All existing keyword argument patterns continue to work.
25
+
26
+ ---
27
+
28
+ ### Migration Guide
29
+
30
+ #### No Changes Required (Backward Compatible)
31
+
32
+ All existing code continues to work:
33
+
34
+ ```ruby
35
+ # ✅ These all work exactly as before:
36
+ client.complete(messages, model: "openai/gpt-4o")
37
+ client.complete(messages, model: "openai/gpt-4o", temperature: 0.7)
38
+ client.stream(messages, model: "openai/gpt-4o") { |chunk| print chunk }
39
+ client.responses("Hello", model: "openai/gpt-4o", reasoning: { effort: "high" })
40
+ ```
41
+
42
+ #### Recommended: Use CompletionOptions for New Code
43
+
44
+ For new code, we recommend using `CompletionOptions` for better IDE support, documentation, and reusability:
45
+
46
+ ```ruby
47
+ # Create reusable configuration
48
+ opts = OpenRouter::CompletionOptions.new(
49
+ model: "openai/gpt-4o",
50
+ temperature: 0.7,
51
+ max_tokens: 1000
52
+ )
53
+
54
+ # Use across multiple calls
55
+ response1 = client.complete(messages1, opts)
56
+ response2 = client.complete(messages2, opts)
57
+
58
+ # Override specific values without mutating original
59
+ creative_opts = opts.merge(temperature: 1.2)
60
+ response3 = client.complete(messages3, creative_opts)
61
+ ```
62
+
63
+ #### Migrating Complex Configurations
64
+
65
+ **Before (v1.x)**:
66
+ ```ruby
67
+ client.complete(
68
+ messages,
69
+ model: "openai/gpt-4o",
70
+ tools: my_tools,
71
+ tool_choice: "auto",
72
+ temperature: 0.7,
73
+ max_tokens: 2000,
74
+ providers: ["openai", "azure"],
75
+ response_format: { type: "json_object" },
76
+ extras: { custom_param: "value" }
77
+ )
78
+ ```
79
+
80
+ **After (v2.0 - recommended)**:
81
+ ```ruby
82
+ opts = OpenRouter::CompletionOptions.new(
83
+ model: "openai/gpt-4o",
84
+ tools: my_tools,
85
+ tool_choice: "auto",
86
+ temperature: 0.7,
87
+ max_tokens: 2000,
88
+ providers: ["openai", "azure"],
89
+ response_format: { type: "json_object" },
90
+ extras: { custom_param: "value" }
91
+ )
92
+
93
+ client.complete(messages, opts)
94
+ ```
95
+
96
+ #### Migrating Streaming Code
97
+
98
+ **Before (v1.x)**:
99
+ ```ruby
100
+ client.stream_complete(
101
+ messages,
102
+ model: "openai/gpt-4o",
103
+ accumulate_response: true
104
+ ) do |chunk|
105
+ print chunk.dig("choices", 0, "delta", "content")
106
+ end
107
+ ```
108
+
109
+ **After (v2.0 - recommended)**:
110
+ ```ruby
111
+ opts = OpenRouter::CompletionOptions.new(model: "openai/gpt-4o")
112
+
113
+ client.stream_complete(messages, opts, accumulate_response: true) do |chunk|
114
+ print chunk.dig("choices", 0, "delta", "content")
115
+ end
116
+
117
+ # Or use the simpler stream method:
118
+ client.stream(messages, opts) { |content| print content }
119
+ ```
120
+
121
+ #### Pattern: Base Options with Per-Request Overrides
122
+
123
+ ```ruby
124
+ # Define base configuration once
125
+ BASE_OPTS = OpenRouter::CompletionOptions.new(
126
+ model: "openai/gpt-4o",
127
+ max_tokens: 1000,
128
+ providers: ["openai"]
129
+ )
130
+
131
+ # Override for specific use cases
132
+ def generate_creative_content(prompt)
133
+ messages = [{ role: "user", content: prompt }]
134
+ client.complete(messages, BASE_OPTS, temperature: 1.2)
135
+ end
136
+
137
+ def generate_factual_content(prompt)
138
+ messages = [{ role: "user", content: prompt }]
139
+ client.complete(messages, BASE_OPTS, temperature: 0.1)
140
+ end
141
+ ```
142
+
143
+ ---
144
+
145
+ ### Added
146
+
147
+ #### `OpenRouter::CompletionOptions` Class
148
+
149
+ A structured configuration object supporting **30+ parameters** organized by category:
150
+
151
+ **Core Parameters**:
152
+ - `model` - Model ID string or array for fallback routing
153
+ - `tools` - Tool/function definitions
154
+ - `tool_choice` - `"auto"`, `"none"`, `"required"`, or specific tool
155
+ - `extras` - Hash for pass-through of any additional/future parameters
156
+
157
+ **Sampling Parameters** (control response randomness):
158
+ - `temperature` - 0.0-2.0, controls randomness (default varies by model)
159
+ - `top_p` - 0.0-1.0, nucleus sampling threshold
160
+ - `top_k` - Integer, limits token selection to top K
161
+ - `frequency_penalty` - -2.0 to 2.0, penalize frequent tokens
162
+ - `presence_penalty` - -2.0 to 2.0, penalize tokens already present
163
+ - `repetition_penalty` - 0.0-2.0, general repetition penalty
164
+ - `min_p` - 0.0-1.0, minimum probability threshold
165
+ - `top_a` - 0.0-1.0, dynamic token filtering
166
+ - `seed` - Integer for reproducible outputs
167
+
168
+ **Output Control**:
169
+ - `max_tokens` - Maximum tokens to generate (legacy)
170
+ - `max_completion_tokens` - Maximum tokens (preferred, newer API)
171
+ - `stop` - String or array of stop sequences
172
+ - `logprobs` - Boolean, return log probabilities
173
+ - `top_logprobs` - 0-20, number of top logprobs per token
174
+ - `logit_bias` - Hash mapping token IDs to bias values (-100 to 100)
175
+ - `response_format` - Structured output schema configuration
176
+ - `parallel_tool_calls` - Boolean, allow parallel function calls
177
+ - `verbosity` - `:low`, `:medium`, `:high`
178
+
179
+ **OpenRouter Routing**:
180
+ - `providers` - Array of provider names (becomes `provider.order`)
181
+ - `provider` - Full provider config hash (overrides `providers`)
182
+ - `transforms` - Array of transform identifiers
183
+ - `plugins` - Array of plugin configs (`web-search`, `response-healing`, etc.)
184
+ - `prediction` - Predicted output for latency optimization
185
+ - `route` - `"fallback"` or `"sort"`
186
+ - `metadata` - Custom key-value metadata
187
+ - `user` - End-user identifier for tracking
188
+ - `session_id` - Session grouping identifier (max 128 chars)
189
+
190
+ **Responses API**:
191
+ - `reasoning` - Hash with `effort:` key (`"minimal"`, `"low"`, `"medium"`, `"high"`)
192
+
193
+ **Client-Side Options** (not sent to API):
194
+ - `force_structured_output` - Override forced extraction mode behavior
195
+
196
+ #### Helper Methods
197
+
198
+ ```ruby
199
+ opts = CompletionOptions.new(model: "gpt-4", tools: [...])
200
+
201
+ opts.has_tools? # => true if tools are defined
202
+ opts.has_response_format? # => true if response_format is set
203
+ opts.fallback_models? # => true if model is an array
204
+
205
+ opts.to_h # => Hash of all non-nil, non-empty values
206
+ opts.to_api_params # => Hash for API request (excludes client-side params)
207
+ opts.merge(temp: 0.5) # => New CompletionOptions with override
208
+ ```
209
+
210
+ ---
211
+
212
+ ### Backward Compatibility
213
+
214
+ **All existing patterns continue to work**:
215
+
216
+ ```ruby
217
+ # Direct kwargs (unchanged from v1.x)
218
+ client.complete(messages, model: "gpt-4")
219
+
220
+ # Hash as second argument
221
+ client.complete(messages, { model: "gpt-4", temperature: 0.7 })
222
+
223
+ # CompletionOptions object (new in v2.0)
224
+ opts = CompletionOptions.new(model: "gpt-4")
225
+ client.complete(messages, opts)
226
+
227
+ # Options with kwargs overrides (new in v2.0)
228
+ client.complete(messages, opts, temperature: 0.9)
229
+ ```
230
+
231
+ The `normalize_options` helper transparently handles all input styles.
232
+
233
+ ---
234
+
235
+ ### Internal Improvements
236
+
237
+ - New `normalize_options` private helper for flexible input handling
238
+ - Refactored `prepare_base_parameters` to accept `CompletionOptions`
239
+ - Refactored `configure_tools_and_structured_outputs!` to use `CompletionOptions`
240
+ - Added focused parameter helpers:
241
+ - `configure_sampling_parameters!`
242
+ - `configure_output_parameters!`
243
+ - `configure_routing_parameters!`
244
+ - Improved separation of concerns in request building
245
+
246
+ ---
247
+
248
+ ### Bug Fixes
249
+
250
+ - Fixed issue where `extras` hash contents were nested incorrectly in API requests. Parameters like `max_tokens` passed via `extras` now correctly appear at the top level of the request body.
251
+
3
252
  ## [1.2.2] - 2025-12-25
4
253
 
5
254
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- open_router_enhanced (1.2.5)
4
+ open_router_enhanced (2.0.0)
5
5
  activesupport (>= 6.0, < 9.0)
6
6
  dotenv (>= 2.0, < 4.0)
7
7
  faraday (>= 1.0, < 3.0)
@@ -11,7 +11,7 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activesupport (8.1.1)
14
+ activesupport (8.1.3)
15
15
  base64
16
16
  bigdecimal
17
17
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -24,11 +24,11 @@ GEM
24
24
  securerandom (>= 0.3)
25
25
  tzinfo (~> 2.0, >= 2.0.5)
26
26
  uri (>= 0.13.1)
27
- addressable (2.8.8)
27
+ addressable (2.9.0)
28
28
  public_suffix (>= 2.0.2, < 8.0)
29
29
  ast (2.4.3)
30
30
  base64 (0.3.0)
31
- bigdecimal (4.0.1)
31
+ bigdecimal (4.1.1)
32
32
  coderay (1.1.3)
33
33
  concurrent-ruby (1.3.6)
34
34
  connection_pool (3.0.2)
@@ -38,42 +38,47 @@ GEM
38
38
  diff-lcs (1.6.2)
39
39
  dotenv (3.2.0)
40
40
  drb (2.2.3)
41
- faraday (2.14.0)
41
+ faraday (2.14.1)
42
42
  faraday-net_http (>= 2.0, < 3.5)
43
43
  json
44
44
  logger
45
- faraday-multipart (1.1.1)
45
+ faraday-multipart (1.2.0)
46
46
  multipart-post (~> 2.0)
47
47
  faraday-net_http (3.4.2)
48
48
  net-http (~> 0.5)
49
49
  hashdiff (1.2.1)
50
50
  i18n (1.14.8)
51
51
  concurrent-ruby (~> 1.0)
52
- json (2.18.0)
52
+ io-console (0.8.2)
53
+ json (2.19.3)
53
54
  json-schema (4.3.1)
54
55
  addressable (>= 2.8)
55
56
  language_server-protocol (3.17.0.5)
56
57
  lint_roller (1.1.0)
57
58
  logger (1.7.0)
58
59
  method_source (1.1.0)
59
- minitest (6.0.0)
60
+ minitest (6.0.4)
61
+ drb (~> 2.0)
60
62
  prism (~> 1.5)
61
63
  multipart-post (2.4.1)
62
64
  net-http (0.9.1)
63
65
  uri (>= 0.11.1)
64
- parallel (1.27.0)
65
- parser (3.3.10.0)
66
+ parallel (2.0.1)
67
+ parser (3.3.11.1)
66
68
  ast (~> 2.4.1)
67
69
  racc
68
- prism (1.7.0)
69
- pry (0.15.2)
70
+ prism (1.9.0)
71
+ pry (0.16.0)
70
72
  coderay (~> 1.1)
71
73
  method_source (~> 1.0)
72
- public_suffix (7.0.0)
74
+ reline (>= 0.6.0)
75
+ public_suffix (7.0.5)
73
76
  racc (1.8.1)
74
77
  rainbow (3.1.1)
75
- rake (13.3.1)
76
- regexp_parser (2.11.3)
78
+ rake (13.4.2)
79
+ regexp_parser (2.12.0)
80
+ reline (0.6.3)
81
+ io-console (~> 0.5)
77
82
  rexml (3.4.4)
78
83
  rspec (3.13.2)
79
84
  rspec-core (~> 3.13.0)
@@ -84,24 +89,24 @@ GEM
84
89
  rspec-expectations (3.13.5)
85
90
  diff-lcs (>= 1.2.0, < 2.0)
86
91
  rspec-support (~> 3.13.0)
87
- rspec-mocks (3.13.7)
92
+ rspec-mocks (3.13.8)
88
93
  diff-lcs (>= 1.2.0, < 2.0)
89
94
  rspec-support (~> 3.13.0)
90
- rspec-support (3.13.6)
91
- rubocop (1.82.1)
95
+ rspec-support (3.13.7)
96
+ rubocop (1.86.1)
92
97
  json (~> 2.3)
93
98
  language_server-protocol (~> 3.17.0.2)
94
99
  lint_roller (~> 1.1.0)
95
- parallel (~> 1.10)
100
+ parallel (>= 1.10)
96
101
  parser (>= 3.3.0.2)
97
102
  rainbow (>= 2.2.2, < 4.0)
98
103
  regexp_parser (>= 2.9.3, < 3.0)
99
- rubocop-ast (>= 1.48.0, < 2.0)
104
+ rubocop-ast (>= 1.49.0, < 2.0)
100
105
  ruby-progressbar (~> 1.7)
101
106
  unicode-display_width (>= 2.4.0, < 4.0)
102
- rubocop-ast (1.48.0)
107
+ rubocop-ast (1.49.1)
103
108
  parser (>= 3.3.7.2)
104
- prism (~> 1.4)
109
+ prism (~> 1.7)
105
110
  ruby-progressbar (1.13.0)
106
111
  securerandom (0.4.1)
107
112
  tzinfo (2.0.6)
@@ -111,7 +116,7 @@ GEM
111
116
  unicode-emoji (4.2.0)
112
117
  uri (1.1.1)
113
118
  vcr (6.4.0)
114
- webmock (3.26.1)
119
+ webmock (3.26.2)
115
120
  addressable (>= 2.8.0)
116
121
  crack (>= 0.3.2)
117
122
  hashdiff (>= 0.4.0, < 2.0.0)
data/README.md CHANGED
@@ -198,6 +198,89 @@ end
198
198
 
199
199
  **[Full configuration documentation](docs/configuration.md)**
200
200
 
201
+ ## CompletionOptions
202
+
203
+ For complex requests with many parameters, use `CompletionOptions` to organize your configuration:
204
+
205
+ ```ruby
206
+ # Create reusable options
207
+ opts = OpenRouter::CompletionOptions.new(
208
+ model: "anthropic/claude-3.5-sonnet",
209
+ temperature: 0.7,
210
+ max_tokens: 1000,
211
+ tools: [weather_tool],
212
+ providers: ["anthropic"]
213
+ )
214
+
215
+ # Use with complete()
216
+ response = client.complete(messages, opts)
217
+
218
+ # Override specific values
219
+ response = client.complete(messages, opts, temperature: 0.9)
220
+
221
+ # All these styles work (backward compatible):
222
+ client.complete(messages, model: "gpt-4") # kwargs
223
+ client.complete(messages, { model: "gpt-4" }) # hash
224
+ client.complete(messages, opts) # options object
225
+ client.complete(messages, opts, temperature: 0.5) # options + override
226
+ ```
227
+
228
+ ### Available Parameters
229
+
230
+ ```ruby
231
+ OpenRouter::CompletionOptions.new(
232
+ # Model selection
233
+ model: "gpt-4", # Model ID or array for fallback
234
+ route: "fallback", # Routing strategy
235
+
236
+ # Sampling parameters
237
+ temperature: 0.7, # 0.0-2.0
238
+ top_p: 0.9, # Nucleus sampling
239
+ top_k: 50, # Top-K sampling
240
+ frequency_penalty: 0.5, # -2.0 to 2.0
241
+ presence_penalty: 0.3, # -2.0 to 2.0
242
+ repetition_penalty: 1.1, # 0.0-2.0
243
+ min_p: 0.05, # Minimum probability
244
+ top_a: 0.1, # Dynamic filtering
245
+ seed: 42, # Reproducibility
246
+
247
+ # Output control
248
+ max_tokens: 1000, # Max completion tokens
249
+ max_completion_tokens: 1000, # Preferred over max_tokens
250
+ stop: ["\n", "END"], # Stop sequences
251
+ logprobs: true, # Return log probabilities
252
+ top_logprobs: 5, # Number of top logprobs
253
+ logit_bias: { "50256" => -100 }, # Token biasing
254
+ response_format: schema, # Structured output
255
+ parallel_tool_calls: true, # Allow parallel calls
256
+
257
+ # Tools
258
+ tools: [weather_tool], # Tool definitions
259
+ tool_choice: "auto", # auto, none, required, or specific
260
+
261
+ # OpenRouter routing
262
+ providers: ["anthropic", "openai"], # Simple provider ordering
263
+ provider: { # Full provider config
264
+ order: ["anthropic"],
265
+ quantizations: ["fp16"],
266
+ allow_fallbacks: true
267
+ },
268
+ transforms: ["middle-out"], # Message transforms
269
+ plugins: [{ id: "web-search" }], # OpenRouter plugins
270
+ prediction: { type: "content", content: "..." }, # Latency optimization
271
+ metadata: { request_id: "abc123" }, # Custom metadata
272
+ user: "user_123", # User identifier
273
+ session_id: "session_456", # Session grouping
274
+
275
+ # Responses API
276
+ reasoning: { effort: "high" }, # For reasoning models
277
+
278
+ # Client-side
279
+ force_structured_output: true, # Force schema injection
280
+ extras: { custom_param: "value" } # Pass-through params
281
+ )
282
+ ```
283
+
201
284
  ## Features
202
285
 
203
286
  ### Tool Calling
@@ -15,6 +15,36 @@ model = OpenRouter::ModelSelector.new
15
15
  response = client.complete(messages, model: model, tools: tools)
16
16
  ```
17
17
 
18
+ ## Using CompletionOptions with Model Selection (v2.0+)
19
+
20
+ Combine model selection with `CompletionOptions` for powerful, reusable configurations:
21
+
22
+ ```ruby
23
+ # Select model dynamically
24
+ model = OpenRouter::ModelSelector.new
25
+ .require(:function_calling, :structured_outputs)
26
+ .optimize_for(:cost)
27
+ .choose
28
+
29
+ # Create reusable options with the selected model
30
+ opts = OpenRouter::CompletionOptions.new(
31
+ model: model,
32
+ tools: my_tools,
33
+ max_tokens: 1000
34
+ )
35
+
36
+ # Use across multiple calls
37
+ response = client.complete(messages, opts)
38
+
39
+ # Or use fallback model arrays directly in options
40
+ fallback_opts = OpenRouter::CompletionOptions.new(
41
+ model: ["anthropic/claude-3.5-sonnet", "openai/gpt-4o", "google/gemini-pro"],
42
+ route: "fallback"
43
+ )
44
+ ```
45
+
46
+ All keyword argument patterns continue to work for backward compatibility.
47
+
18
48
  ## ModelSelector API
19
49
 
20
50
  The `ModelSelector` class provides a fluent interface for building complex model selection criteria.
data/docs/plugins.md CHANGED
@@ -31,6 +31,40 @@ response = client.complete(
31
31
  )
32
32
  ```
33
33
 
34
+ ## Using CompletionOptions with Plugins (v2.0+)
35
+
36
+ For cleaner, reusable configurations, use the `CompletionOptions` class:
37
+
38
+ ```ruby
39
+ # Create reusable plugin configuration
40
+ web_search_opts = OpenRouter::CompletionOptions.new(
41
+ model: "openai/gpt-4o-mini",
42
+ plugins: [{ id: "web-search" }]
43
+ )
44
+
45
+ # Use across multiple calls
46
+ response = client.complete(messages, web_search_opts)
47
+
48
+ # Combine multiple plugins with other options
49
+ research_opts = OpenRouter::CompletionOptions.new(
50
+ model: "openai/gpt-4o",
51
+ plugins: [
52
+ { id: "web-search" },
53
+ { id: "pdf-inputs" }
54
+ ],
55
+ max_tokens: 2000,
56
+ temperature: 0.3
57
+ )
58
+
59
+ # Add prediction for latency optimization
60
+ fast_opts = OpenRouter::CompletionOptions.new(
61
+ model: "openai/gpt-4o",
62
+ prediction: { type: "content", content: "The answer is..." }
63
+ )
64
+ ```
65
+
66
+ All keyword argument patterns continue to work for backward compatibility.
67
+
34
68
  ## Response Healing Plugin
35
69
 
36
70
  The response-healing plugin fixes common JSON formatting issues server-side:
@@ -17,6 +17,40 @@ response = client.responses(
17
17
  puts response.content # => "Paris"
18
18
  ```
19
19
 
20
+ ## Using CompletionOptions (v2.0+)
21
+
22
+ For cleaner, reusable configurations, use the `CompletionOptions` class:
23
+
24
+ ```ruby
25
+ # Create reusable reasoning configuration
26
+ reasoning_opts = OpenRouter::CompletionOptions.new(
27
+ model: "openai/o4-mini",
28
+ reasoning: { effort: "high" },
29
+ max_tokens: 1000
30
+ )
31
+
32
+ # Use with responses
33
+ response = client.responses("Explain quantum entanglement", reasoning_opts)
34
+
35
+ # Override specific values per-request
36
+ response = client.responses(
37
+ "What is 15% of 80?",
38
+ reasoning_opts,
39
+ reasoning: { effort: "low" } # Simpler problem, less reasoning needed
40
+ )
41
+
42
+ # Create tool-enabled responses configuration
43
+ tool_opts = OpenRouter::CompletionOptions.new(
44
+ model: "openai/gpt-4o-mini",
45
+ tools: [weather_tool, calculator_tool],
46
+ tool_choice: "auto"
47
+ )
48
+
49
+ response = client.responses("What's the weather in Tokyo?", tool_opts)
50
+ ```
51
+
52
+ All keyword argument patterns continue to work for backward compatibility.
53
+
20
54
  ## With Reasoning
21
55
 
22
56
  The Responses API supports reasoning with configurable effort levels:
data/docs/streaming.md CHANGED
@@ -22,6 +22,38 @@ response = streaming_client.stream_complete(
22
22
  puts response.content # Complete response after streaming
23
23
  ```
24
24
 
25
+ ## Using CompletionOptions (v2.0+)
26
+
27
+ For cleaner, reusable configurations, use the `CompletionOptions` class:
28
+
29
+ ```ruby
30
+ # Create reusable streaming configuration
31
+ stream_opts = OpenRouter::CompletionOptions.new(
32
+ model: "openai/gpt-4o-mini",
33
+ temperature: 0.8,
34
+ max_tokens: 2000
35
+ )
36
+
37
+ # Use with stream_complete
38
+ response = streaming_client.stream_complete(
39
+ messages,
40
+ stream_opts,
41
+ accumulate_response: true
42
+ )
43
+
44
+ # Use with simple stream method
45
+ streaming_client.stream(messages, stream_opts) do |chunk|
46
+ print chunk
47
+ end
48
+
49
+ # Override specific values per-request
50
+ streaming_client.stream(messages, stream_opts, temperature: 0.2) do |chunk|
51
+ print chunk
52
+ end
53
+ ```
54
+
55
+ All keyword argument patterns continue to work for backward compatibility.
56
+
25
57
  ## Streaming with Callbacks
26
58
 
27
59
  The streaming client supports extensive callback events for monitoring and custom processing.
@@ -35,6 +35,37 @@ puts user["age"] # => 30
35
35
  puts user["email"] # => "john@example.com"
36
36
  ```
37
37
 
38
+ ## Using CompletionOptions (v2.0+)
39
+
40
+ For cleaner, reusable configurations, use the `CompletionOptions` class:
41
+
42
+ ```ruby
43
+ # Create reusable structured output configuration
44
+ struct_opts = OpenRouter::CompletionOptions.new(
45
+ model: "openai/gpt-4o",
46
+ response_format: user_schema,
47
+ max_tokens: 500,
48
+ temperature: 0.1 # Lower temperature for more deterministic outputs
49
+ )
50
+
51
+ # Use across multiple calls
52
+ response1 = client.complete(messages1, struct_opts)
53
+ response2 = client.complete(messages2, struct_opts)
54
+
55
+ # Override for specific requests
56
+ creative_opts = struct_opts.merge(temperature: 0.8)
57
+ response3 = client.complete(messages3, creative_opts)
58
+
59
+ # Control forced structured output behavior
60
+ opts_with_force = OpenRouter::CompletionOptions.new(
61
+ model: "some-model",
62
+ response_format: schema,
63
+ force_structured_output: true # Override auto-detection
64
+ )
65
+ ```
66
+
67
+ All keyword argument patterns continue to work for backward compatibility.
68
+
38
69
  ## Key Concepts: JSON Content vs Structured Outputs
39
70
 
40
71
  **Important: These are fundamentally different features.**