raix 0.4.0 → 0.4.2

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: 318057e8ece37b63c06884a61c37dc1ef15f38cee2c05d5305bcaf6d3697420e
4
- data.tar.gz: 250229d71a808203689b87cea7c1ce8dd31b085c92b54dce392787299cd420d6
3
+ metadata.gz: cd88f295667264948d2710fb7eb0bcd74e5ab0177e76678314c34958e79fa6bd
4
+ data.tar.gz: b86495b4b67c5259915a7ef20502005d90ededf6be991821703d1d83e876c572
5
5
  SHA512:
6
- metadata.gz: dc51d8fab907f8ffa5e95df2ef308ee3cd5fc443f46803e8a6f184be80e12719be2d25aeb51349a7912b35df55866db66cdec532c0258bc66a97aeabbc4017d0
7
- data.tar.gz: 7b393143a5da05ba75ac11a8a77e31b0e61c1e7e9cb564fc6c986a4f63e2f98a24d43056f613e8e4685baab85251c9e819bcf99fcdc22ee7428b50eb9895d4e7
6
+ metadata.gz: cf1982b065312860c046a363486169a3d4572b65bd5ded3e82e270e3cbb689a173d6ff94cbcc897d6d8e5f27ef7e6558193ea3b266c18cc5bd1a8f2ca54fa6cd
7
+ data.tar.gz: 58eb7f54eb7d3a2656dbdd3e44a977c04b1dfb5ef5f7bd549ece96ecce9a931007edea5d364f189a1916883838c6a674e794799365914a1a5cab2402994da5f1
data/CHANGELOG.md CHANGED
@@ -14,3 +14,7 @@
14
14
 
15
15
  ## [0.4.0] - 2024-10-18
16
16
  - adds support for Anthropic-style prompt caching
17
+ - defaults to `max_completion_tokens` when using OpenAI directly
18
+
19
+ ## [0.4.2] - 2024-11-05
20
+ - adds support for [Predicted Outputs](https://platform.openai.com/docs/guides/latency-optimization#use-predicted-outputs) with the `prediction` option for OpenAI
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- raix (0.3.2)
4
+ raix (0.4.2)
5
5
  activesupport (>= 6.0)
6
6
  open_router (~> 0.2)
7
7
 
data/README.md CHANGED
@@ -42,6 +42,14 @@ 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
+ ### Predicted Outputs
46
+
47
+ Raix supports [Predicted Outputs](https://platform.openai.com/docs/guides/latency-optimization#use-predicted-outputs) with the `prediction` parameter for OpenAI.
48
+
49
+ ```ruby
50
+ >> ai.chat_completion(openai: "gpt-4o", params: { prediction: })
51
+ ```
52
+
45
53
  ### Prompt Caching
46
54
 
47
55
  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".
@@ -2,10 +2,11 @@
2
2
 
3
3
  require "active_support/concern"
4
4
  require "active_support/core_ext/object/blank"
5
- require "raix/message_adapters/base"
6
5
  require "open_router"
7
6
  require "openai"
8
7
 
8
+ require_relative "message_adapters/base"
9
+
9
10
  module Raix
10
11
  # The `ChatCompletion`` module is a Rails concern that provides a way to interact
11
12
  # with the OpenRouter Chat Completion API via its client. The module includes a few
@@ -19,7 +20,7 @@ module Raix
19
20
  extend ActiveSupport::Concern
20
21
 
21
22
  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
+ :prediction, :repetition_penalty, :response_format, :stream, :temperature, :max_completion_tokens,
23
24
  :max_tokens, :seed, :stop, :top_a, :top_k, :top_logprobs, :top_p, :tools, :tool_choice, :provider
24
25
 
25
26
  # This method performs chat completion based on the provided transcript and parameters.
@@ -39,6 +40,7 @@ module Raix
39
40
  params[:max_completion_tokens] ||= max_completion_tokens.presence || Raix.configuration.max_completion_tokens
40
41
  params[:max_tokens] ||= max_tokens.presence || Raix.configuration.max_tokens
41
42
  params[:min_p] ||= min_p.presence
43
+ params[:prediction] = { type: "content", content: params[:prediction] || prediction } if params[:prediction] || prediction.present?
42
44
  params[:presence_penalty] ||= presence_penalty.presence
43
45
  params[:provider] ||= provider.presence
44
46
  params[:repetition_penalty] ||= repetition_penalty.presence
@@ -149,8 +151,12 @@ module Raix
149
151
  private
150
152
 
151
153
  def openai_request(params:, model:, messages:)
152
- # deprecated in favor of max_completion_tokens
153
- params.delete(:max_tokens)
154
+ if params[:prediction]
155
+ params.delete(:max_completion_tokens)
156
+ else
157
+ params[:max_completion_tokens] ||= params[:max_tokens]
158
+ params.delete(:max_tokens)
159
+ end
154
160
 
155
161
  params[:stream] ||= stream.presence
156
162
  params[:stream_options] = { include_usage: true } if params[:stream]
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/delegation"
4
+
5
+ module Raix
6
+ module MessageAdapters
7
+ # Transforms messages into the format expected by the OpenAI API
8
+ class Base
9
+ attr_accessor :context
10
+
11
+ delegate :cache_at, :model, to: :context
12
+
13
+ def initialize(context)
14
+ @context = context
15
+ end
16
+
17
+ def transform(message)
18
+ return message if message[:role].present?
19
+
20
+ if message[:function].present?
21
+ { role: "assistant", name: message.dig(:function, :name), content: message.dig(:function, :arguments).to_json }
22
+ elsif message[:result].present?
23
+ { role: "function", name: message[:name], content: message[:result] }
24
+ else
25
+ content(message)
26
+ end
27
+ end
28
+
29
+ protected
30
+
31
+ def content(message)
32
+ case message
33
+ in { system: content }
34
+ { role: "system", content: }
35
+ in { user: content }
36
+ { role: "user", content: }
37
+ in { assistant: content }
38
+ { role: "assistant", content: }
39
+ else
40
+ raise ArgumentError, "Invalid message format: #{message.inspect}"
41
+ end.tap do |msg|
42
+ # convert to anthropic multipart format if model is claude-3 and cache_at is set
43
+ if model.to_s.include?("anthropic/claude-3") && cache_at && msg[:content].to_s.length > cache_at.to_i
44
+ msg[:content] = [{ type: "text", text: msg[:content], cache_control: { type: "ephemeral" } }]
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/deep_dup"
4
+
5
+ module Raix
6
+ # Handles the formatting of responses for AI interactions.
7
+ #
8
+ # This class is responsible for converting input data into a JSON schema
9
+ # that can be used to structure and validate AI responses. It supports
10
+ # nested structures and arrays, ensuring that the output conforms to
11
+ # the expected format for AI model interactions.
12
+ #
13
+ # @example
14
+ # input = { name: { type: "string" }, age: { type: "integer" } }
15
+ # format = ResponseFormat.new("PersonInfo", input)
16
+ # schema = format.to_schema
17
+ #
18
+ # @attr_reader [String] name The name of the response format
19
+ # @attr_reader [Hash] input The input data to be formatted
20
+ class ResponseFormat
21
+ def initialize(name, input)
22
+ @name = name
23
+ @input = input
24
+ end
25
+
26
+ def to_json(*)
27
+ JSON.pretty_generate(to_schema)
28
+ end
29
+
30
+ def to_schema
31
+ {
32
+ type: "json_schema",
33
+ json_schema: {
34
+ name: @name,
35
+ schema: {
36
+ type: "object",
37
+ properties: decode(@input.deep_dup),
38
+ required: @input.keys,
39
+ additionalProperties: false
40
+ },
41
+ strict: true
42
+ }
43
+ }
44
+ end
45
+
46
+ private
47
+
48
+ def decode(input)
49
+ {}.tap do |response|
50
+ case input
51
+ when Array
52
+ properties = {}
53
+ input.each { |item| properties.merge!(decode(item)) }
54
+
55
+ response[:type] = "array"
56
+ response[:items] = {
57
+ type: "object",
58
+ properties:,
59
+ required: properties.keys.select { |key| properties[key].delete(:required) },
60
+ additionalProperties: false
61
+ }
62
+ when Hash
63
+ input.each do |key, value|
64
+ response[key] = if value.is_a?(Hash) && value.key?(:type)
65
+ value
66
+ else
67
+ decode(value)
68
+ end
69
+ end
70
+ else
71
+ raise "Invalid input"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
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.4.0"
4
+ VERSION = "0.4.2"
5
5
  end
data/lib/raix.rb CHANGED
@@ -4,6 +4,7 @@ require_relative "raix/version"
4
4
  require_relative "raix/chat_completion"
5
5
  require_relative "raix/function_dispatch"
6
6
  require_relative "raix/prompt_declarations"
7
+ require_relative "raix/response_format"
7
8
 
8
9
  # The Raix module provides configuration options for the Raix gem.
9
10
  module Raix
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.0
4
+ version: 0.4.2
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-10-19 00:00:00.000000000 Z
11
+ date: 2024-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -58,7 +58,9 @@ files:
58
58
  - lib/raix.rb
59
59
  - lib/raix/chat_completion.rb
60
60
  - lib/raix/function_dispatch.rb
61
+ - lib/raix/message_adapters/base.rb
61
62
  - lib/raix/prompt_declarations.rb
63
+ - lib/raix/response_format.rb
62
64
  - lib/raix/version.rb
63
65
  - raix.gemspec
64
66
  - sig/raix.rbs
@@ -84,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
86
  - !ruby/object:Gem::Version
85
87
  version: '0'
86
88
  requirements: []
87
- rubygems_version: 3.5.21
89
+ rubygems_version: 3.4.10
88
90
  signing_key:
89
91
  specification_version: 4
90
92
  summary: Ruby AI eXtensions