raix 2.0.2 → 2.0.4
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 +4 -4
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +4 -6
- data/lib/raix/chat_completion.rb +33 -12
- data/lib/raix/version.rb +1 -1
- metadata +2 -3
- data/raix.gemspec +0 -43
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ec3b7449e51440e1d669e323dea0c371a3d18d7d4083090d61599ff1dbce3bbe
|
|
4
|
+
data.tar.gz: 6f2029218c2099ab1172d1d458be9ccb33836078d5abc9b04865cae0d497bc07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 76e1d77e80023dbc634e4b2f72eaa269f05d539ea97470df1355e69e73d75a6cf4bd2bd7e627ab2cf01fcb381fd85c4b153d28005f32190ff1b8ae69c3bea275
|
|
7
|
+
data.tar.gz: 6fedf4bae5c406b72b83a1aacbc985849eb7c461237e7cc1e12d9ca73ea05a861912f13739bc92ba8ad992bf88f5a31fa530dc9468c5bdb4763f53caf3833843
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [2.0.4] - 2026-05-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- `ruby_llm_request` now preserves the upstream provider's `id`, `model`, and `provider` fields, plus the full `usage` payload (including `prompt_tokens_details.cached_tokens` and `completion_tokens_details.reasoning_tokens`) on the OpenAI-compatible response hash. Previously the conversion dropped everything except `choices` and basic token counts, which broke callers that needed the generation id for authoritative cost lookups (e.g. OpenRouter's `/api/v1/generation` endpoint) or that wanted to verify prompt-cache hits via `cached_tokens`.
|
|
7
|
+
- Added `require "active_support/core_ext/module/delegation"` so `Raix::ChatCompletion` loads cleanly without an external preload of ActiveSupport. The class uses `delegate :configuration, to: :class` but did not pull in the required core ext, so a bare `require "raix"` would raise `NoMethodError` for `delegate`.
|
|
8
|
+
|
|
9
|
+
## [2.0.3] - 2026-04-30
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- `NoMethodError: undefined method 'strip' for nil` in `Raix::ChatCompletion` when an LLM (notably Gemini under certain stop conditions) returns a final assistant message with `"content": null`. Three call sites in `lib/raix/chat_completion.rb` now use `content.to_s.strip` so a nil response coerces to `""` instead of raising.
|
|
13
|
+
|
|
1
14
|
## [2.0.2] - 2026-03-27
|
|
2
15
|
|
|
3
16
|
### Fixed
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
raix (2.0.
|
|
4
|
+
raix (2.0.4)
|
|
5
5
|
activesupport (>= 6.0)
|
|
6
6
|
faraday-retry (~> 2.0)
|
|
7
7
|
ostruct
|
|
@@ -49,8 +49,8 @@ GEM
|
|
|
49
49
|
net-http
|
|
50
50
|
faraday-retry (2.4.0)
|
|
51
51
|
faraday (~> 2.0)
|
|
52
|
-
ffi (1.17.2)
|
|
53
52
|
ffi (1.17.2-arm64-darwin)
|
|
53
|
+
ffi (1.17.2-x86_64-linux-gnu)
|
|
54
54
|
formatador (1.1.0)
|
|
55
55
|
guard (2.18.1)
|
|
56
56
|
formatador (>= 0.2.4)
|
|
@@ -83,18 +83,16 @@ GEM
|
|
|
83
83
|
lumberjack (1.2.10)
|
|
84
84
|
marcel (1.1.0)
|
|
85
85
|
method_source (1.1.0)
|
|
86
|
-
mini_portile2 (2.8.9)
|
|
87
86
|
minitest (5.27.0)
|
|
88
87
|
multipart-post (2.4.1)
|
|
89
88
|
nenv (0.3.0)
|
|
90
89
|
net-http (0.4.1)
|
|
91
90
|
uri
|
|
92
91
|
netrc (0.11.0)
|
|
93
|
-
nokogiri (1.18.8)
|
|
94
|
-
mini_portile2 (~> 2.8.2)
|
|
95
|
-
racc (~> 1.4)
|
|
96
92
|
nokogiri (1.18.8-arm64-darwin)
|
|
97
93
|
racc (~> 1.4)
|
|
94
|
+
nokogiri (1.18.8-x86_64-linux-gnu)
|
|
95
|
+
racc (~> 1.4)
|
|
98
96
|
notiffany (0.1.3)
|
|
99
97
|
nenv (~> 0.1)
|
|
100
98
|
shellany (~> 0.0)
|
data/lib/raix/chat_completion.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "active_support/concern"
|
|
|
4
4
|
require "active_support/core_ext/object/blank"
|
|
5
5
|
require "active_support/core_ext/string/filters"
|
|
6
6
|
require "active_support/core_ext/hash/indifferent_access"
|
|
7
|
+
require "active_support/core_ext/module/delegation"
|
|
7
8
|
require "ruby_llm"
|
|
8
9
|
|
|
9
10
|
module Raix
|
|
@@ -56,9 +57,7 @@ module Raix
|
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
# Instance level access to the class-level configuration.
|
|
59
|
-
|
|
60
|
-
self.class.configuration
|
|
61
|
-
end
|
|
60
|
+
delegate :configuration, to: :class
|
|
62
61
|
|
|
63
62
|
# This method performs chat completion based on the provided transcript and parameters.
|
|
64
63
|
#
|
|
@@ -174,7 +173,7 @@ module Raix
|
|
|
174
173
|
# Process the final response
|
|
175
174
|
content = response.dig("choices", 0, "message", "content")
|
|
176
175
|
transcript << { assistant: content } if save_response
|
|
177
|
-
return raw ? response : content.strip
|
|
176
|
+
return raw ? response : content.to_s.strip
|
|
178
177
|
end
|
|
179
178
|
|
|
180
179
|
# Dispatch tool calls
|
|
@@ -215,7 +214,7 @@ module Raix
|
|
|
215
214
|
|
|
216
215
|
content = response.dig("choices", 0, "message", "content")
|
|
217
216
|
transcript << { assistant: content } if save_response
|
|
218
|
-
return raw ? response : content.strip
|
|
217
|
+
return raw ? response : content.to_s.strip
|
|
219
218
|
end
|
|
220
219
|
end
|
|
221
220
|
|
|
@@ -223,7 +222,7 @@ module Raix
|
|
|
223
222
|
content = res.dig("choices", 0, "message", "content")
|
|
224
223
|
|
|
225
224
|
transcript << { assistant: content } if save_response
|
|
226
|
-
content = content.strip
|
|
225
|
+
content = content.to_s.strip
|
|
227
226
|
|
|
228
227
|
if json
|
|
229
228
|
# Make automatic JSON parsing available to non-OpenAI providers that don't support the response_format parameter
|
|
@@ -398,8 +397,34 @@ module Raix
|
|
|
398
397
|
# Non-streaming mode - return OpenAI-compatible response format
|
|
399
398
|
response_message = has_user_message ? chat.complete : chat.ask
|
|
400
399
|
|
|
401
|
-
#
|
|
400
|
+
# Pull through the raw provider payload when available. OpenRouter's
|
|
401
|
+
# `id` is the only handle we have to look up authoritative billing
|
|
402
|
+
# cost via /api/v1/generation, and callers that watch the response
|
|
403
|
+
# snapshot for `model` / cached-token counts shouldn't have to break
|
|
404
|
+
# out of the OpenAI-compatible shape to get them.
|
|
405
|
+
raw_body = response_message.raw.respond_to?(:body) ? response_message.raw.body : nil
|
|
406
|
+
raw_body = {} unless raw_body.is_a?(Hash)
|
|
407
|
+
|
|
408
|
+
usage_payload = {
|
|
409
|
+
"prompt_tokens" => response_message.input_tokens,
|
|
410
|
+
"completion_tokens" => response_message.output_tokens,
|
|
411
|
+
"total_tokens" => (response_message.input_tokens || 0) + (response_message.output_tokens || 0)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
# Merge prompt_tokens_details / completion_tokens_details (cached tokens,
|
|
415
|
+
# reasoning tokens) when the provider supplied them.
|
|
416
|
+
if (upstream_usage = raw_body["usage"]).is_a?(Hash)
|
|
417
|
+
upstream_usage.each do |key, value|
|
|
418
|
+
next if usage_payload.key?(key)
|
|
419
|
+
|
|
420
|
+
usage_payload[key] = value
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
402
424
|
{
|
|
425
|
+
"id" => raw_body["id"],
|
|
426
|
+
"model" => raw_body["model"] || response_message.model_id,
|
|
427
|
+
"provider" => raw_body["provider"],
|
|
403
428
|
"choices" => [
|
|
404
429
|
{
|
|
405
430
|
"message" => {
|
|
@@ -410,11 +435,7 @@ module Raix
|
|
|
410
435
|
"finish_reason" => response_message.tool_call? ? "tool_calls" : "stop"
|
|
411
436
|
}
|
|
412
437
|
],
|
|
413
|
-
"usage" =>
|
|
414
|
-
"prompt_tokens" => response_message.input_tokens,
|
|
415
|
-
"completion_tokens" => response_message.output_tokens,
|
|
416
|
-
"total_tokens" => (response_message.input_tokens || 0) + (response_message.output_tokens || 0)
|
|
417
|
-
}
|
|
438
|
+
"usage" => usage_payload
|
|
418
439
|
}
|
|
419
440
|
end
|
|
420
441
|
rescue StandardError => e
|
data/lib/raix/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: raix
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Obie Fernandez
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-05-19 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|
|
@@ -114,7 +114,6 @@ files:
|
|
|
114
114
|
- lib/raix/response_format.rb
|
|
115
115
|
- lib/raix/transcript_adapter.rb
|
|
116
116
|
- lib/raix/version.rb
|
|
117
|
-
- raix.gemspec
|
|
118
117
|
- sig/raix.rbs
|
|
119
118
|
homepage: https://github.com/OlympiaAI/raix
|
|
120
119
|
licenses:
|
data/raix.gemspec
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "lib/raix/version"
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = "raix"
|
|
7
|
-
spec.version = Raix::VERSION
|
|
8
|
-
spec.authors = ["Obie Fernandez"]
|
|
9
|
-
spec.email = ["obiefernandez@gmail.com"]
|
|
10
|
-
|
|
11
|
-
spec.summary = "Ruby AI eXtensions"
|
|
12
|
-
spec.homepage = "https://github.com/OlympiaAI/raix"
|
|
13
|
-
spec.license = "MIT"
|
|
14
|
-
spec.required_ruby_version = ">= 3.2.2"
|
|
15
|
-
|
|
16
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
17
|
-
spec.metadata["source_code_uri"] = "https://github.com/OlympiaAI/raix"
|
|
18
|
-
spec.metadata["changelog_uri"] = "https://github.com/OlympiaAI/raix/blob/main/CHANGELOG.md"
|
|
19
|
-
|
|
20
|
-
# Specify which files should be added to the gem when it is released.
|
|
21
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
22
|
-
spec.files = Dir.chdir(__dir__) do
|
|
23
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
|
24
|
-
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Ensure all gem files are world-readable so they work in Docker containers
|
|
29
|
-
# where gems are installed as root but the app runs as a non-root user.
|
|
30
|
-
spec.files.each do |f|
|
|
31
|
-
path = File.join(__dir__, f)
|
|
32
|
-
File.chmod(0o644, path) if File.file?(path) && !File.executable?(path)
|
|
33
|
-
end
|
|
34
|
-
spec.bindir = "exe"
|
|
35
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
36
|
-
spec.require_paths = ["lib"]
|
|
37
|
-
|
|
38
|
-
spec.add_dependency "activesupport", ">= 6.0"
|
|
39
|
-
spec.add_dependency "faraday-retry", "~> 2.0"
|
|
40
|
-
spec.add_dependency "ostruct"
|
|
41
|
-
spec.add_dependency "ruby_llm", "~> 1.9"
|
|
42
|
-
spec.add_dependency "zeitwerk", "~> 2.7"
|
|
43
|
-
end
|