rager 0.6.0 → 0.7.0

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -27
  3. data/lib/rager/chat/message.rb +10 -0
  4. data/lib/rager/chat/message_content.rb +7 -0
  5. data/lib/rager/chat/message_delta.rb +7 -0
  6. data/lib/rager/chat/options.rb +6 -7
  7. data/lib/rager/chat/providers/openai.rb +49 -34
  8. data/lib/rager/chat/schema.rb +0 -2
  9. data/lib/rager/config.rb +12 -20
  10. data/lib/rager/context.rb +302 -186
  11. data/lib/rager/context_options.rb +23 -0
  12. data/lib/rager/embed/options.rb +3 -3
  13. data/lib/rager/embed/providers/openai.rb +7 -3
  14. data/lib/rager/errors/options_error.rb +1 -1
  15. data/lib/rager/http/adapters/async_http.rb +0 -2
  16. data/lib/rager/http/adapters/mock.rb +0 -2
  17. data/lib/rager/http/adapters/net_http.rb +18 -19
  18. data/lib/rager/{image_gen → image}/options.rb +4 -4
  19. data/lib/rager/{image_gen → image}/output_format.rb +1 -1
  20. data/lib/rager/{image_gen → image}/providers/abstract.rb +4 -4
  21. data/lib/rager/{image_gen → image}/providers/replicate.rb +14 -10
  22. data/lib/rager/{logger.rb → log_strategy.rb} +2 -1
  23. data/lib/rager/{mesh_gen → mesh}/options.rb +3 -3
  24. data/lib/rager/{mesh_gen → mesh}/providers/abstract.rb +4 -4
  25. data/lib/rager/{mesh_gen → mesh}/providers/replicate.rb +13 -9
  26. data/lib/rager/operation.rb +2 -2
  27. data/lib/rager/options.rb +1 -1
  28. data/lib/rager/outcome.rb +25 -0
  29. data/lib/rager/providers.rb +37 -25
  30. data/lib/rager/rerank/input.rb +20 -0
  31. data/lib/rager/rerank/options.rb +2 -2
  32. data/lib/rager/rerank/providers/abstract.rb +2 -3
  33. data/lib/rager/rerank/providers/cohere.rb +19 -13
  34. data/lib/rager/rerank/result.rb +20 -0
  35. data/lib/rager/result.rb +92 -122
  36. data/lib/rager/search/options.rb +3 -2
  37. data/lib/rager/search/providers/jina.rb +16 -15
  38. data/lib/rager/search/result.rb +21 -0
  39. data/lib/rager/template/input.rb +20 -0
  40. data/lib/rager/template/options.rb +1 -1
  41. data/lib/rager/template/providers/abstract.rb +2 -3
  42. data/lib/rager/template/providers/erb.rb +4 -5
  43. data/lib/rager/template/providers/mustache.rb +30 -0
  44. data/lib/rager/types.rb +31 -24
  45. data/lib/rager/utils/http.rb +73 -15
  46. data/lib/rager/utils/replicate.rb +28 -6
  47. data/lib/rager/utils/runtime.rb +21 -0
  48. data/lib/rager/version.rb +1 -1
  49. metadata +18 -12
  50. data/lib/rager/rerank/output.rb +0 -13
  51. data/lib/rager/search/output.rb +0 -14
@@ -23,27 +23,28 @@ module Rager
23
23
  def make_request(request)
24
24
  uri = URI(request.url)
25
25
 
26
- Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
27
- if request.timeout
28
- http.open_timeout = request.timeout
29
- http.read_timeout = request.timeout
30
- end
26
+ http = Net::HTTP.new(uri.host, uri.port)
27
+ http.use_ssl = uri.scheme == "https"
31
28
 
32
- http_request = build_http_request(request, uri)
29
+ if request.timeout
30
+ http.open_timeout = request.timeout
31
+ http.read_timeout = request.timeout
32
+ end
33
33
 
34
- http.request(http_request) do |response|
35
- body = if request.streaming
36
- body_enum(response)
37
- else
38
- response.body
39
- end
34
+ http_request = build_http_request(request, uri)
40
35
 
41
- return Rager::Http::Response.new(
42
- status: response.code.to_i,
43
- headers: response.to_hash,
44
- body: body
45
- )
36
+ http.request(http_request) do |response|
37
+ body = if request.streaming
38
+ body_enum(response)
39
+ else
40
+ response.body
46
41
  end
42
+
43
+ return Rager::Http::Response.new(
44
+ status: response.code.to_i,
45
+ headers: response.to_hash,
46
+ body: body
47
+ )
47
48
  end
48
49
  rescue SocketError => e
49
50
  raise Rager::Errors::HttpError.new(
@@ -95,8 +96,6 @@ module Rager
95
96
  )
96
97
  end
97
98
 
98
- private
99
-
100
99
  sig {
101
100
  params(
102
101
  response: Net::HTTPResponse
@@ -4,16 +4,16 @@
4
4
  require "sorbet-runtime"
5
5
 
6
6
  module Rager
7
- module ImageGen
7
+ module Image
8
8
  class Options < T::Struct
9
9
  extend T::Sig
10
10
  include Rager::Options
11
11
 
12
- const :provider, String, default: "replicate"
12
+ const :provider, Symbol, default: :replicate
13
13
  const :model, String, default: "black-forest-labs/flux-schnell"
14
- const :output_format, T.nilable(Rager::ImageGen::OutputFormat)
15
- const :api_key, T.nilable(String)
14
+ const :output_format, T.nilable(Rager::Image::OutputFormat)
16
15
  const :seed, T.nilable(Integer)
16
+ const :api_key, T.nilable(String)
17
17
  const :timeout, T.nilable(Numeric)
18
18
  end
19
19
  end
@@ -4,7 +4,7 @@
4
4
  require "sorbet-runtime"
5
5
 
6
6
  module Rager
7
- module ImageGen
7
+ module Image
8
8
  class OutputFormat < T::Enum
9
9
  extend T::Sig
10
10
 
@@ -4,7 +4,7 @@
4
4
  require "sorbet-runtime"
5
5
 
6
6
  module Rager
7
- module ImageGen
7
+ module Image
8
8
  module Providers
9
9
  class Abstract
10
10
  extend T::Sig
@@ -14,10 +14,10 @@ module Rager
14
14
  sig do
15
15
  abstract.params(
16
16
  prompt: String,
17
- options: Rager::ImageGen::Options
18
- ).returns(Rager::Types::ImageGenOutput)
17
+ options: Rager::Image::Options
18
+ ).returns(Rager::Types::ImageOutput)
19
19
  end
20
- def image_gen(prompt, options)
20
+ def image(prompt, options)
21
21
  end
22
22
  end
23
23
  end
@@ -5,16 +5,24 @@ require "sorbet-runtime"
5
5
  require "json"
6
6
 
7
7
  module Rager
8
- module ImageGen
8
+ module Image
9
9
  module Providers
10
- class Replicate < Rager::ImageGen::Providers::Abstract
10
+ class Replicate < Rager::Image::Providers::Abstract
11
11
  extend T::Sig
12
12
 
13
- sig { override.params(prompt: String, options: Rager::ImageGen::Options).returns(Rager::Types::ImageGenOutput) }
14
- def image_gen(prompt, options)
13
+ sig { override.params(prompt: String, options: Rager::Image::Options).returns(Rager::Types::ImageOutput) }
14
+ def image(prompt, options)
15
15
  api_key = options.api_key || ENV["REPLICATE_API_KEY"]
16
16
  raise Rager::Errors::CredentialsError.new("Replicate", env_var: ["REPLICATE_API_KEY"]) if api_key.nil?
17
17
 
18
+ url = "https://api.replicate.com/v1/models/#{options.model}/predictions"
19
+
20
+ headers = {
21
+ "Authorization" => "Bearer #{api_key}",
22
+ "Content-Type" => "application/json",
23
+ "Prefer" => "wait"
24
+ }
25
+
18
26
  body = {
19
27
  input: {
20
28
  prompt: prompt
@@ -25,13 +33,9 @@ module Rager
25
33
  end
26
34
 
27
35
  request = Rager::Http::Request.new(
28
- url: "https://api.replicate.com/v1/models/#{options.model}/predictions",
36
+ url: url,
29
37
  verb: Rager::Http::Verb::Post,
30
- headers: {
31
- "Authorization" => "Bearer #{api_key}",
32
- "Content-Type" => "application/json",
33
- "Prefer" => "wait"
34
- },
38
+ headers: headers,
35
39
  body: body.to_json,
36
40
  timeout: options.timeout || Rager.config.timeout
37
41
  )
@@ -4,8 +4,9 @@
4
4
  require "sorbet-runtime"
5
5
 
6
6
  module Rager
7
- class Logger < T::Enum
7
+ class LogStrategy < T::Enum
8
8
  enums do
9
+ None = new("none")
9
10
  Stdout = new("stdout")
10
11
  Remote = new("remote")
11
12
  end
@@ -4,16 +4,16 @@
4
4
  require "sorbet-runtime"
5
5
 
6
6
  module Rager
7
- module MeshGen
7
+ module Mesh
8
8
  class Options < T::Struct
9
9
  extend T::Sig
10
10
  include Rager::Options
11
11
 
12
- const :provider, String, default: "replicate"
12
+ const :provider, Symbol, default: :replicate
13
13
  const :model, String, default: "firoz/trellis"
14
14
  const :version, String, default: "4876f2a8da1c544772dffa32e8889da4a1bab3a1f5c1937bfcfccb99ae347251"
15
- const :api_key, T.nilable(String)
16
15
  const :seed, T.nilable(Integer)
16
+ const :api_key, T.nilable(String)
17
17
  const :timeout, T.nilable(Numeric)
18
18
  end
19
19
  end
@@ -4,7 +4,7 @@
4
4
  require "sorbet-runtime"
5
5
 
6
6
  module Rager
7
- module MeshGen
7
+ module Mesh
8
8
  module Providers
9
9
  class Abstract
10
10
  extend T::Sig
@@ -14,10 +14,10 @@ module Rager
14
14
  sig do
15
15
  abstract.params(
16
16
  image_url: String,
17
- options: Rager::MeshGen::Options
18
- ).returns(Rager::Types::MeshGenOutput)
17
+ options: Rager::Mesh::Options
18
+ ).returns(Rager::Types::MeshOutput)
19
19
  end
20
- def mesh_gen(image_url, options)
20
+ def mesh(image_url, options)
21
21
  end
22
22
  end
23
23
  end
@@ -6,16 +6,23 @@ require "json"
6
6
  require "sorbet-runtime"
7
7
 
8
8
  module Rager
9
- module MeshGen
9
+ module Mesh
10
10
  module Providers
11
- class Replicate < Rager::MeshGen::Providers::Abstract
11
+ class Replicate < Rager::Mesh::Providers::Abstract
12
12
  extend T::Sig
13
13
 
14
- sig { override.params(image_url: String, options: Rager::MeshGen::Options).returns(Rager::Types::MeshGenOutput) }
15
- def mesh_gen(image_url, options)
14
+ sig { override.params(image_url: String, options: Rager::Mesh::Options).returns(Rager::Types::MeshOutput) }
15
+ def mesh(image_url, options)
16
16
  api_key = options.api_key || ENV["REPLICATE_API_KEY"]
17
17
  raise Rager::Errors::CredentialsError.new("Replicate", env_var: ["REPLICATE_API_KEY"]) if api_key.nil?
18
18
 
19
+ url = "https://api.replicate.com/v1/predictions"
20
+
21
+ headers = {
22
+ "Authorization" => "Bearer #{api_key}",
23
+ "Content-Type" => "application/json"
24
+ }
25
+
19
26
  body = {
20
27
  version: options.version,
21
28
  input: {
@@ -31,12 +38,9 @@ module Rager
31
38
  end
32
39
 
33
40
  request = Rager::Http::Request.new(
34
- url: "https://api.replicate.com/v1/predictions",
41
+ url: url,
35
42
  verb: Rager::Http::Verb::Post,
36
- headers: {
37
- "Authorization" => "Bearer #{api_key}",
38
- "Content-Type" => "application/json"
39
- },
43
+ headers: headers,
40
44
  body: body.to_json,
41
45
  timeout: options.timeout || Rager.config.timeout
42
46
  )
@@ -8,8 +8,8 @@ module Rager
8
8
  enums do
9
9
  Chat = new("chat")
10
10
  Embed = new("embed")
11
- ImageGen = new("image_gen")
12
- MeshGen = new("mesh_gen")
11
+ Image = new("image")
12
+ Mesh = new("mesh")
13
13
  Rerank = new("rerank")
14
14
  Search = new("search")
15
15
  Template = new("template")
data/lib/rager/options.rb CHANGED
@@ -9,7 +9,7 @@ module Rager
9
9
  extend T::Helpers
10
10
  requires_ancestor { T::Struct }
11
11
 
12
- sig { returns(T::Hash[String, T.untyped]) }
12
+ sig { returns(T::Hash[Symbol, T.untyped]) }
13
13
  def serialize_safe
14
14
  result = T.cast(self, T::Struct).serialize
15
15
  result["api_key"] = "[REDACTED]" if result.key?("api_key")
@@ -0,0 +1,25 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Rager
7
+ class Outcome < T::Struct
8
+ extend T::Sig
9
+
10
+ const :context_id, String
11
+ const :value, Float
12
+ const :feedback, T.nilable(String)
13
+ const :tags, T::Array[T.untyped], default: []
14
+
15
+ sig { params(options: T.untyped).returns(String) }
16
+ def to_json(options = nil)
17
+ {
18
+ context_id: context_id,
19
+ value: value,
20
+ feedback: feedback,
21
+ tags: tags
22
+ }.to_json(options)
23
+ end
24
+ end
25
+ end
@@ -8,42 +8,54 @@ module Rager
8
8
  extend T::Sig
9
9
 
10
10
  PROVIDERS = T.let({
11
- chat: {
12
- "openai" => -> { Rager::Chat::Providers::Openai.new }
11
+ Rager::Operation::Chat => {
12
+ openai: -> { Rager::Chat::Providers::Openai.new }
13
13
  },
14
- embed: {
15
- "openai" => -> { Rager::Embed::Providers::Openai.new }
14
+ Rager::Operation::Embed => {
15
+ openai: -> { Rager::Embed::Providers::Openai.new }
16
16
  },
17
- rerank: {
18
- "cohere" => -> { Rager::Rerank::Providers::Cohere.new }
17
+ Rager::Operation::Rerank => {
18
+ cohere: -> { Rager::Rerank::Providers::Cohere.new }
19
19
  },
20
- image_gen: {
21
- "replicate" => -> { Rager::ImageGen::Providers::Replicate.new }
20
+ Rager::Operation::Image => {
21
+ replicate: -> { Rager::Image::Providers::Replicate.new }
22
22
  },
23
- mesh_gen: {
24
- "replicate" => -> { Rager::MeshGen::Providers::Replicate.new }
23
+ Rager::Operation::Mesh => {
24
+ replicate: -> { Rager::Mesh::Providers::Replicate.new }
25
25
  },
26
- template: {
27
- "erb" => -> { Rager::Template::Providers::Erb.new }
26
+ Rager::Operation::Template => {
27
+ erb: -> { Rager::Template::Providers::Erb.new },
28
+ mustache: -> { Rager::Template::Providers::Mustache.new }
28
29
  },
29
- search: {
30
- "jina" => -> { Rager::Search::Providers::Jina.new }
30
+ Rager::Operation::Search => {
31
+ jina: -> { Rager::Search::Providers::Jina.new }
31
32
  }
32
- }.freeze, T::Hash[Symbol, T::Hash[String, T.proc.returns(T.untyped)]])
33
+ }, T::Hash[Rager::Operation, T::Hash[Symbol, T.proc.returns(T.untyped)]])
33
34
 
34
- sig { params(operation: Symbol, provider_name: String, options: Rager::Options).returns(T.untyped) }
35
- def self.get_provider(operation, provider_name, options)
35
+ sig { params(operation: Rager::Operation).returns(T::Array[Symbol]) }
36
+ def self.list_providers(operation)
37
+ PROVIDERS.fetch(operation, {}).keys
38
+ end
39
+
40
+ sig {
41
+ params(
42
+ operation: Rager::Operation,
43
+ key: Symbol,
44
+ options: Rager::Types::Options
45
+ ).returns(T.untyped)
46
+ }
47
+ def self.get_provider(operation, key, options)
36
48
  operation_providers = PROVIDERS[operation]
37
- if operation_providers&.key?(provider_name.downcase)
38
- return T.must(operation_providers[provider_name.downcase]).call
49
+
50
+ unless operation_providers&.key?(key)
51
+ raise Rager::Errors::OptionsError.new(
52
+ options,
53
+ ["provider"],
54
+ details: "Unknown #{operation} provider: #{key}. Known providers: #{list_providers(operation).join(", ")}"
55
+ )
39
56
  end
40
57
 
41
- known_providers = operation_providers&.keys&.join(", ") || "none"
42
- raise Rager::Errors::OptionsError.new(
43
- options,
44
- ["provider"],
45
- details: "Unknown #{operation} provider: #{provider_name}. Known providers: #{known_providers}"
46
- )
58
+ T.must(operation_providers[key]).call
47
59
  end
48
60
  end
49
61
  end
@@ -0,0 +1,20 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Rager
7
+ module Rerank
8
+ class Input < T::Struct
9
+ extend T::Sig
10
+
11
+ const :query, String
12
+ const :documents, T::Array[String]
13
+
14
+ sig { params(options: T.untyped).returns(String) }
15
+ def to_json(options = nil)
16
+ serialize.to_json(options)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -9,10 +9,10 @@ module Rager
9
9
  extend T::Sig
10
10
  include Rager::Options
11
11
 
12
- const :provider, String, default: "cohere"
13
- const :url, T.nilable(String)
12
+ const :provider, Symbol, default: :cohere
14
13
  const :model, T.nilable(String)
15
14
  const :n, T.nilable(Integer)
15
+ const :url, T.nilable(String)
16
16
  const :api_key, T.nilable(String)
17
17
  const :timeout, T.nilable(Numeric)
18
18
  end
@@ -13,12 +13,11 @@ module Rager
13
13
 
14
14
  sig do
15
15
  abstract.params(
16
- query: String,
17
- documents: T::Array[String],
16
+ input: Rager::Rerank::Input,
18
17
  options: Rager::Rerank::Options
19
18
  ).returns(Rager::Types::RerankOutput)
20
19
  end
21
- def rerank(query, documents, options)
20
+ def rerank(input, options)
22
21
  end
23
22
  end
24
23
  end
@@ -13,31 +13,31 @@ module Rager
13
13
 
14
14
  sig do
15
15
  override.params(
16
- query: String,
17
- documents: T::Array[String],
16
+ input: Rager::Rerank::Input,
18
17
  options: Rager::Rerank::Options
19
18
  ).returns(Rager::Types::RerankOutput)
20
19
  end
21
- def rerank(query, documents, options)
20
+ def rerank(input, options)
22
21
  api_key = options.api_key || ENV["COHERE_API_KEY"]
23
22
  raise Rager::Errors::CredentialsError.new("Cohere", env_var: ["COHERE_API_KEY"]) if api_key.nil?
24
23
 
25
24
  base_url = options.url || ENV["COHERE_URL"] || "https://api.cohere.com/v2"
25
+ url = "#{base_url}/rerank"
26
+
27
+ headers = {"Content-Type" => "application/json"}
28
+ headers["Authorization"] = "Bearer #{api_key}" if api_key
26
29
 
27
30
  body = {
28
31
  model: options.model || "rerank-v3.5",
29
- query: query,
30
- documents: documents
32
+ query: input.query,
33
+ documents: input.documents
31
34
  }.tap do |b|
32
35
  b[:top_n] = options.n if options.n
33
36
  end
34
37
 
35
- headers = {"Content-Type" => "application/json"}
36
- headers["Authorization"] = "Bearer #{api_key}" if api_key
37
-
38
38
  request = Rager::Http::Request.new(
39
39
  verb: Rager::Http::Verb::Post,
40
- url: "#{base_url}/rerank",
40
+ url: url,
41
41
  headers: headers,
42
42
  body: body.to_json,
43
43
  timeout: options.timeout || Rager.config.timeout
@@ -49,12 +49,18 @@ module Rager
49
49
  raise Rager::Errors::HttpError.new(Rager.config.http_adapter, request.url, response.status, body: response_body) if response.status != 200
50
50
 
51
51
  parsed_response = JSON.parse(response_body)
52
- results = parsed_response["results"] || []
52
+ results = parsed_response["results"]
53
53
 
54
- scores = results.map { |r| r["relevance_score"].to_f }
55
- reranked_documents = results.map { |r| documents[r["index"]] }
54
+ results.map do |r|
55
+ score = r["relevance_score"].to_f
56
+ content = input.documents[r["index"]]
57
+ next if content.nil?
56
58
 
57
- Rager::Rerank::Output.new(scores: scores, documents: reranked_documents)
59
+ Rager::Rerank::Result.new(
60
+ score: score,
61
+ content: content
62
+ )
63
+ end
58
64
  end
59
65
  end
60
66
  end
@@ -0,0 +1,20 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "sorbet-runtime"
5
+
6
+ module Rager
7
+ module Rerank
8
+ class Result < T::Struct
9
+ extend T::Sig
10
+
11
+ const :score, Float
12
+ const :content, String
13
+
14
+ sig { params(options: T.untyped).returns(String) }
15
+ def to_json(options = nil)
16
+ serialize.to_json(options)
17
+ end
18
+ end
19
+ end
20
+ end