raix 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +2 -1
- data/README.md +36 -0
- data/lib/raix/chat_completion.rb +34 -30
- data/lib/raix/function_dispatch.rb +1 -1
- data/lib/raix/prompt_declarations.rb +1 -1
- data/lib/raix/version.rb +1 -1
- data/lib/raix.rb +5 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 318057e8ece37b63c06884a61c37dc1ef15f38cee2c05d5305bcaf6d3697420e
|
4
|
+
data.tar.gz: 250229d71a808203689b87cea7c1ce8dd31b085c92b54dce392787299cd420d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc51d8fab907f8ffa5e95df2ef308ee3cd5fc443f46803e8a6f184be80e12719be2d25aeb51349a7912b35df55866db66cdec532c0258bc66a97aeabbc4017d0
|
7
|
+
data.tar.gz: 7b393143a5da05ba75ac11a8a77e31b0e61c1e7e9cb564fc6c986a4f63e2f98a24d43056f613e8e4685baab85251c9e819bcf99fcdc22ee7428b50eb9895d4e7
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -42,6 +42,30 @@ transcript << { role: "user", content: "What is the meaning of life?" }
|
|
42
42
|
|
43
43
|
One of the advantages of OpenRouter and the reason that it is used by default by this library is that it handles mapping message formats from the OpenAI standard to whatever other model you're wanting to use (Anthropic, Cohere, etc.)
|
44
44
|
|
45
|
+
### Prompt Caching
|
46
|
+
|
47
|
+
Raix supports [Anthropic-style prompt caching](https://openrouter.ai/docs/prompt-caching#anthropic-claude) when using Anthropic's Claud family of models. You can specify a `cache_at` parameter when doing a chat completion. If the character count for the content of a particular message is longer than the cache_at parameter, it will be sent to Anthropic as a multipart message with a cache control "breakpoint" set to "ephemeral".
|
48
|
+
|
49
|
+
Note that there is a limit of four breakpoints, and the cache will expire within five minutes. Therefore, it is recommended to reserve the cache breakpoints for large bodies of text, such as character cards, CSV data, RAG data, book chapters, etc. Raix does not enforce a limit on the number of breakpoints, which means that you might get an error if you try to cache too many messages.
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
>> my_class.chat_completion(params: { cache_at: 1000 })
|
53
|
+
=> {
|
54
|
+
"messages": [
|
55
|
+
{
|
56
|
+
"role": "system",
|
57
|
+
"content": [
|
58
|
+
{
|
59
|
+
"type": "text",
|
60
|
+
"text": "HUGE TEXT BODY LONGER THAN 1000 CHARACTERS",
|
61
|
+
"cache_control": {
|
62
|
+
"type": "ephemeral"
|
63
|
+
}
|
64
|
+
}
|
65
|
+
]
|
66
|
+
},
|
67
|
+
```
|
68
|
+
|
45
69
|
### Use of Tools/Functions
|
46
70
|
|
47
71
|
The second (optional) module that you can add to your Ruby classes after `ChatCompletion` is `FunctionDispatch`. It lets you declare and implement functions to be called at the AI's discretion as part of a chat completion "loop" in a declarative, Rails-like "DSL" fashion.
|
@@ -216,6 +240,18 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
216
240
|
|
217
241
|
$ gem install raix
|
218
242
|
|
243
|
+
If you are using the default OpenRouter API, Raix expects `Raix.configuration.openrouter_client` to initialized with the OpenRouter API client instance.
|
244
|
+
|
245
|
+
You can add an initializer to your application's `config/initializers` directory:
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
# config/initializers/raix.rb
|
249
|
+
Raix.configure do |config|
|
250
|
+
config.openrouter_client = OpenRouter::Client.new
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
You will also need to configure the OpenRouter API access token as per the instructions here: https://github.com/OlympiaAI/open_router?tab=readme-ov-file#quickstart
|
219
255
|
|
220
256
|
## Development
|
221
257
|
|
data/lib/raix/chat_completion.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "active_support/concern"
|
4
4
|
require "active_support/core_ext/object/blank"
|
5
|
+
require "raix/message_adapters/base"
|
5
6
|
require "open_router"
|
6
7
|
require "openai"
|
7
8
|
|
@@ -17,9 +18,9 @@ module Raix
|
|
17
18
|
module ChatCompletion
|
18
19
|
extend ActiveSupport::Concern
|
19
20
|
|
20
|
-
attr_accessor :frequency_penalty, :logit_bias, :logprobs, :loop, :min_p, :model, :presence_penalty,
|
21
|
-
:repetition_penalty, :response_format, :stream, :temperature, :
|
22
|
-
:top_k, :top_logprobs, :top_p, :tools, :tool_choice, :provider
|
21
|
+
attr_accessor :cache_at, :frequency_penalty, :logit_bias, :logprobs, :loop, :min_p, :model, :presence_penalty,
|
22
|
+
:repetition_penalty, :response_format, :stream, :temperature, :max_completion_tokens,
|
23
|
+
:max_tokens, :seed, :stop, :top_a, :top_k, :top_logprobs, :top_p, :tools, :tool_choice, :provider
|
23
24
|
|
24
25
|
# This method performs chat completion based on the provided transcript and parameters.
|
25
26
|
#
|
@@ -30,16 +31,12 @@ module Raix
|
|
30
31
|
# @option params [Boolean] :raw (false) Whether to return the raw response or dig the text content.
|
31
32
|
# @return [String|Hash] The completed chat response.
|
32
33
|
def chat_completion(params: {}, loop: false, json: false, raw: false, openai: false)
|
33
|
-
messages = transcript.flatten.compact.map { |msg| transform_message_format(msg) }
|
34
|
-
raise "Can't complete an empty transcript" if messages.blank?
|
35
|
-
|
36
|
-
# used by FunctionDispatch
|
37
|
-
self.loop = loop
|
38
|
-
|
39
34
|
# set params to default values if not provided
|
35
|
+
params[:cache_at] ||= cache_at.presence
|
40
36
|
params[:frequency_penalty] ||= frequency_penalty.presence
|
41
37
|
params[:logit_bias] ||= logit_bias.presence
|
42
38
|
params[:logprobs] ||= logprobs.presence
|
39
|
+
params[:max_completion_tokens] ||= max_completion_tokens.presence || Raix.configuration.max_completion_tokens
|
43
40
|
params[:max_tokens] ||= max_tokens.presence || Raix.configuration.max_tokens
|
44
41
|
params[:min_p] ||= min_p.presence
|
45
42
|
params[:presence_penalty] ||= presence_penalty.presence
|
@@ -57,23 +54,29 @@ module Raix
|
|
57
54
|
params[:top_p] ||= top_p.presence
|
58
55
|
|
59
56
|
if json
|
60
|
-
|
61
|
-
|
57
|
+
unless openai
|
58
|
+
params[:provider] ||= {}
|
59
|
+
params[:provider][:require_parameters] = true
|
60
|
+
end
|
62
61
|
params[:response_format] ||= {}
|
63
62
|
params[:response_format][:type] = "json_object"
|
64
63
|
end
|
65
64
|
|
65
|
+
# used by FunctionDispatch
|
66
|
+
self.loop = loop
|
67
|
+
|
66
68
|
# set the model to the default if not provided
|
67
69
|
self.model ||= Raix.configuration.model
|
68
70
|
|
71
|
+
adapter = MessageAdapters::Base.new(self)
|
72
|
+
messages = transcript.flatten.compact.map { |msg| adapter.transform(msg) }
|
73
|
+
raise "Can't complete an empty transcript" if messages.blank?
|
74
|
+
|
69
75
|
begin
|
70
76
|
response = if openai
|
71
|
-
openai_request(params:, model: openai,
|
72
|
-
messages:)
|
77
|
+
openai_request(params:, model: openai, messages:)
|
73
78
|
else
|
74
|
-
openrouter_request(
|
75
|
-
params:, model:, messages:
|
76
|
-
)
|
79
|
+
openrouter_request(params:, model:, messages:)
|
77
80
|
end
|
78
81
|
retry_count = 0
|
79
82
|
content = nil
|
@@ -115,8 +118,8 @@ module Raix
|
|
115
118
|
raise e # just fail if we can't get content after 3 attempts
|
116
119
|
end
|
117
120
|
|
118
|
-
|
119
|
-
|
121
|
+
puts "Bad JSON received!!!!!!: #{content}"
|
122
|
+
raise e
|
120
123
|
rescue Faraday::BadRequestError => e
|
121
124
|
# make sure we see the actual error message on console or Honeybadger
|
122
125
|
puts "Chat completion failed!!!!!!!!!!!!!!!!: #{e.response[:body]}"
|
@@ -132,6 +135,9 @@ module Raix
|
|
132
135
|
# { user: "Hey what time is it?" },
|
133
136
|
# { assistant: "Sorry, pumpkins do not wear watches" }
|
134
137
|
#
|
138
|
+
# to add a function call use the following format:
|
139
|
+
# { function: { name: 'fancy_pants_function', arguments: { param: 'value' } } }
|
140
|
+
#
|
135
141
|
# to add a function result use the following format:
|
136
142
|
# { function: result, name: 'fancy_pants_function' }
|
137
143
|
#
|
@@ -143,11 +149,21 @@ module Raix
|
|
143
149
|
private
|
144
150
|
|
145
151
|
def openai_request(params:, model:, messages:)
|
152
|
+
# deprecated in favor of max_completion_tokens
|
153
|
+
params.delete(:max_tokens)
|
154
|
+
|
146
155
|
params[:stream] ||= stream.presence
|
156
|
+
params[:stream_options] = { include_usage: true } if params[:stream]
|
157
|
+
|
158
|
+
params.delete(:temperature) if model == "o1-preview"
|
159
|
+
|
147
160
|
Raix.configuration.openai_client.chat(parameters: params.compact.merge(model:, messages:))
|
148
161
|
end
|
149
162
|
|
150
163
|
def openrouter_request(params:, model:, messages:)
|
164
|
+
# max_completion_tokens is not supported by OpenRouter
|
165
|
+
params.delete(:max_completion_tokens)
|
166
|
+
|
151
167
|
retry_count = 0
|
152
168
|
|
153
169
|
begin
|
@@ -163,17 +179,5 @@ module Raix
|
|
163
179
|
raise e
|
164
180
|
end
|
165
181
|
end
|
166
|
-
|
167
|
-
def transform_message_format(message)
|
168
|
-
return message if message[:role].present?
|
169
|
-
|
170
|
-
if message[:function].present?
|
171
|
-
{ role: "assistant", name: message.dig(:function, :name), content: message.dig(:function, :arguments).to_json }
|
172
|
-
elsif message[:result].present?
|
173
|
-
{ role: "function", name: message[:name], content: message[:result] }
|
174
|
-
else
|
175
|
-
{ role: message.first.first, content: message.first.last }
|
176
|
-
end
|
177
|
-
end
|
178
182
|
end
|
179
183
|
end
|
@@ -35,7 +35,7 @@ module Raix
|
|
35
35
|
# argument will be executed in the instance context of the class that includes this module.
|
36
36
|
#
|
37
37
|
# Example:
|
38
|
-
# function :google_search,
|
38
|
+
# function :google_search, "Search Google for something", query: { type: "string" } do |arguments|
|
39
39
|
# GoogleSearch.new(arguments[:query]).search
|
40
40
|
# end
|
41
41
|
#
|
@@ -83,7 +83,7 @@ module Raix
|
|
83
83
|
params = @current_prompt.params.merge(params)
|
84
84
|
|
85
85
|
# set the stream if necessary
|
86
|
-
self.stream = instance_exec(
|
86
|
+
self.stream = instance_exec(&@current_prompt.stream) if @current_prompt.stream.present?
|
87
87
|
|
88
88
|
super(params:, raw:).then do |response|
|
89
89
|
transcript << { assistant: response }
|
data/lib/raix/version.rb
CHANGED
data/lib/raix.rb
CHANGED
@@ -16,6 +16,9 @@ module Raix
|
|
16
16
|
# The max_tokens option determines the maximum number of tokens to generate.
|
17
17
|
attr_accessor :max_tokens
|
18
18
|
|
19
|
+
# The max_completion_tokens option determines the maximum number of tokens to generate.
|
20
|
+
attr_accessor :max_completion_tokens
|
21
|
+
|
19
22
|
# The model option determines the model to use for text generation. This option
|
20
23
|
# is normally set in each class that includes the ChatCompletion module.
|
21
24
|
attr_accessor :model
|
@@ -27,12 +30,14 @@ module Raix
|
|
27
30
|
attr_accessor :openai_client
|
28
31
|
|
29
32
|
DEFAULT_MAX_TOKENS = 1000
|
33
|
+
DEFAULT_MAX_COMPLETION_TOKENS = 16_384
|
30
34
|
DEFAULT_MODEL = "meta-llama/llama-3-8b-instruct:free"
|
31
35
|
DEFAULT_TEMPERATURE = 0.0
|
32
36
|
|
33
37
|
# Initializes a new instance of the Configuration class with default values.
|
34
38
|
def initialize
|
35
39
|
self.temperature = DEFAULT_TEMPERATURE
|
40
|
+
self.max_completion_tokens = DEFAULT_MAX_COMPLETION_TOKENS
|
36
41
|
self.max_tokens = DEFAULT_MAX_TOKENS
|
37
42
|
self.model = DEFAULT_MODEL
|
38
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: raix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Obie Fernandez
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
84
|
- !ruby/object:Gem::Version
|
85
85
|
version: '0'
|
86
86
|
requirements: []
|
87
|
-
rubygems_version: 3.
|
87
|
+
rubygems_version: 3.5.21
|
88
88
|
signing_key:
|
89
89
|
specification_version: 4
|
90
90
|
summary: Ruby AI eXtensions
|