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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29e66d0046995dca8f9d093ccfd719e54905f94c62d5ee4e78588e1b40b0dadd
4
- data.tar.gz: 7b1d4544576891124e209130d339be55fe35573e05dcb05474c526655e850390
3
+ metadata.gz: 318057e8ece37b63c06884a61c37dc1ef15f38cee2c05d5305bcaf6d3697420e
4
+ data.tar.gz: 250229d71a808203689b87cea7c1ce8dd31b085c92b54dce392787299cd420d6
5
5
  SHA512:
6
- metadata.gz: a8989d6e4c4422054a2e452637ffc3205a7242a060f32c86fcf5c72ff1050f50d7bc6b20a9e4eb147398cdbc461c6fe80cf1f1426e0401fe944ce484ae70d4f9
7
- data.tar.gz: 364ba4571799b8a7eea4abae9f4a905a32121303f62e22e66bff9f4663831b6311efa3eb9238f4eaa326456c2db467d7de0277000e42a11db710435c3e482679
6
+ metadata.gz: dc51d8fab907f8ffa5e95df2ef308ee3cd5fc443f46803e8a6f184be80e12719be2d25aeb51349a7912b35df55866db66cdec532c0258bc66a97aeabbc4017d0
7
+ data.tar.gz: 7b393143a5da05ba75ac11a8a77e31b0e61c1e7e9cb564fc6c986a4f63e2f98a24d43056f613e8e4685baab85251c9e819bcf99fcdc22ee7428b50eb9895d4e7
data/.rubocop.yml CHANGED
@@ -11,7 +11,7 @@ Style/StringLiteralsInInterpolation:
11
11
  EnforcedStyle: double_quotes
12
12
 
13
13
  Layout/LineLength:
14
- Max: 120
14
+ Max: 180
15
15
 
16
16
  Metrics/BlockLength:
17
17
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -8,3 +8,9 @@
8
8
  - adds `ChatCompletion` module
9
9
  - adds `PromptDeclarations` module
10
10
  - adds `FunctionDispatch` module
11
+
12
+ ## [0.3.2] - 2024-06-29
13
+ - adds support for streaming
14
+
15
+ ## [0.4.0] - 2024-10-18
16
+ - adds support for Anthropic-style prompt caching
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- raix (0.3.1)
4
+ raix (0.3.2)
5
5
  activesupport (>= 6.0)
6
6
  open_router (~> 0.2)
7
7
 
@@ -198,6 +198,7 @@ GEM
198
198
 
199
199
  PLATFORMS
200
200
  arm64-darwin-21
201
+ arm64-darwin-22
201
202
  x86_64-linux
202
203
 
203
204
  DEPENDENCIES
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
 
@@ -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, :max_tokens, :seed, :stop, :top_a,
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
- params[:provider] ||= {}
61
- params[:provider][:require_parameters] = true
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
- # attempt to fix the JSON
119
- JsonFixer.new.call(content, e.message)
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, description: "Search Google for something", query: { type: "string" } do |arguments|
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(&current_prompt.stream) if current_prompt.stream.present?
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Raix
4
- VERSION = "0.3.2"
4
+ VERSION = "0.4.0"
5
5
  end
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.3.2
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-06-30 00:00:00.000000000 Z
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.4.10
87
+ rubygems_version: 3.5.21
88
88
  signing_key:
89
89
  specification_version: 4
90
90
  summary: Ruby AI eXtensions