rager 0.5.0 → 0.6.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.
- checksums.yaml +4 -4
- data/README.md +50 -7
- data/lib/rager/chat/options.rb +9 -5
- data/lib/rager/chat/providers/openai.rb +27 -26
- data/lib/rager/chat/schema.rb +3 -2
- data/lib/rager/config.rb +5 -1
- data/lib/rager/context.rb +168 -55
- data/lib/rager/embed/options.rb +1 -0
- data/lib/rager/embed/providers/openai.rb +8 -4
- data/lib/rager/errors/credentials_error.rb +24 -0
- data/lib/rager/errors/dependency_error.rb +23 -0
- data/lib/rager/errors/http_error.rb +12 -5
- data/lib/rager/errors/options_error.rb +10 -5
- data/lib/rager/errors/parse_error.rb +9 -5
- data/lib/rager/errors/template_error.rb +10 -4
- data/lib/rager/errors/timeout_error.rb +25 -0
- data/lib/rager/http/adapters/async_http.rb +67 -13
- data/lib/rager/http/adapters/mock.rb +43 -45
- data/lib/rager/http/adapters/net_http.rb +145 -0
- data/lib/rager/http/request.rb +2 -0
- data/lib/rager/image_gen/options.rb +1 -0
- data/lib/rager/image_gen/providers/replicate.rb +5 -4
- data/lib/rager/mesh_gen/options.rb +1 -0
- data/lib/rager/mesh_gen/providers/replicate.rb +7 -5
- data/lib/rager/providers.rb +49 -0
- data/lib/rager/rerank/options.rb +1 -0
- data/lib/rager/rerank/{query.rb → output.rb} +2 -2
- data/lib/rager/rerank/providers/abstract.rb +3 -2
- data/lib/rager/rerank/providers/cohere.rb +17 -14
- data/lib/rager/result.rb +49 -29
- data/lib/rager/search/options.rb +3 -1
- data/lib/rager/search/output.rb +14 -0
- data/lib/rager/search/providers/jina.rb +67 -0
- data/lib/rager/template/providers/abstract.rb +3 -2
- data/lib/rager/template/providers/erb.rb +6 -5
- data/lib/rager/types.rb +11 -7
- data/lib/rager/utils/http.rb +35 -28
- data/lib/rager/utils/replicate.rb +13 -16
- data/lib/rager/version.rb +1 -1
- metadata +10 -30
- data/lib/rager/chat.rb +0 -35
- data/lib/rager/embed.rb +0 -35
- data/lib/rager/errors/missing_credentials_error.rb +0 -19
- data/lib/rager/errors/unknown_provider_error.rb +0 -17
- data/lib/rager/image_gen.rb +0 -31
- data/lib/rager/mesh_gen.rb +0 -31
- data/lib/rager/rerank/result.rb +0 -13
- data/lib/rager/rerank.rb +0 -35
- data/lib/rager/search/providers/brave.rb +0 -59
- data/lib/rager/search/result.rb +0 -14
- data/lib/rager/search.rb +0 -35
- data/lib/rager/template/input.rb +0 -11
- data/lib/rager/template.rb +0 -35
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "json"
|
5
|
+
|
5
6
|
require "sorbet-runtime"
|
6
7
|
|
7
8
|
module Rager
|
@@ -18,7 +19,9 @@ module Rager
|
|
18
19
|
end
|
19
20
|
def embed(text, options)
|
20
21
|
api_key = options.api_key || ENV["OPENAI_API_KEY"]
|
21
|
-
raise Rager::Errors::
|
22
|
+
raise Rager::Errors::CredentialsError.new("OpenAI", env_var: ["OPENAI_API_KEY"]) if api_key.nil?
|
23
|
+
|
24
|
+
base_url = options.url || ENV["OPENAI_URL"] || "https://api.openai.com/v1"
|
22
25
|
|
23
26
|
headers = {"Content-Type" => "application/json"}
|
24
27
|
headers["Authorization"] = "Bearer #{api_key}" if api_key
|
@@ -30,15 +33,16 @@ module Rager
|
|
30
33
|
|
31
34
|
request = Rager::Http::Request.new(
|
32
35
|
verb: Rager::Http::Verb::Post,
|
33
|
-
url:
|
36
|
+
url: "#{base_url}/embeddings",
|
34
37
|
headers: headers,
|
35
|
-
body: body.to_json
|
38
|
+
body: body.to_json,
|
39
|
+
timeout: options.timeout || Rager.config.timeout
|
36
40
|
)
|
37
41
|
|
38
42
|
response = Rager.config.http_adapter.make_request(request)
|
39
43
|
response_body = T.cast(T.must(response.body), String)
|
40
44
|
|
41
|
-
raise Rager::Errors::HttpError.new(Rager.config.http_adapter, response.status, response_body) if response.status != 200
|
45
|
+
raise Rager::Errors::HttpError.new(Rager.config.http_adapter, request.url, response.status, body: response_body) if response.status != 200
|
42
46
|
|
43
47
|
parsed_response = JSON.parse(response_body)
|
44
48
|
parsed_response["data"].map { |item| item["embedding"] }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
6
|
+
module Rager
|
7
|
+
module Errors
|
8
|
+
class CredentialsError < Rager::Error
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { params(provider: String, env_var: T::Array[String], details: T.nilable(String)).void }
|
12
|
+
def initialize(provider, env_var: [], details: nil)
|
13
|
+
error_data = {
|
14
|
+
type: "credentials",
|
15
|
+
provider: provider,
|
16
|
+
env_var: env_var,
|
17
|
+
details: details
|
18
|
+
}
|
19
|
+
|
20
|
+
super(error_data.to_json)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
6
|
+
module Rager
|
7
|
+
module Errors
|
8
|
+
class DependencyError < Rager::Error
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { params(dependency: String, details: T.nilable(String)).void }
|
12
|
+
def initialize(dependency, details: nil)
|
13
|
+
error_data = {
|
14
|
+
type: "dependency",
|
15
|
+
dependency: dependency,
|
16
|
+
details: details
|
17
|
+
}
|
18
|
+
|
19
|
+
super(error_data.to_json)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -8,11 +8,18 @@ module Rager
|
|
8
8
|
class HttpError < Rager::Error
|
9
9
|
extend T::Sig
|
10
10
|
|
11
|
-
sig { params(adapter: Rager::Http::Adapters::Abstract, status: Integer, body: T.nilable(String)).void }
|
12
|
-
def initialize(adapter, status, body)
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
sig { params(adapter: Rager::Http::Adapters::Abstract, url: T.nilable(String), status: Integer, body: T.nilable(String), details: T.nilable(String)).void }
|
12
|
+
def initialize(adapter, url, status, body: nil, details: nil)
|
13
|
+
error_data = {
|
14
|
+
type: "http",
|
15
|
+
adapter: adapter.class.name,
|
16
|
+
url: url,
|
17
|
+
status: status,
|
18
|
+
body: body,
|
19
|
+
details: details
|
20
|
+
}
|
21
|
+
|
22
|
+
super(error_data.to_json)
|
16
23
|
end
|
17
24
|
end
|
18
25
|
end
|
@@ -8,11 +8,16 @@ module Rager
|
|
8
8
|
class OptionsError < Rager::Error
|
9
9
|
extend T::Sig
|
10
10
|
|
11
|
-
sig { params(invalid_keys: T::Array[String],
|
12
|
-
def initialize(invalid_keys
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
sig { params(options: Rager::Options, invalid_keys: T::Array[String], details: T.nilable(String)).void }
|
12
|
+
def initialize(options, invalid_keys, details: nil)
|
13
|
+
error_data = {
|
14
|
+
type: "options",
|
15
|
+
options: options.serialize_safe,
|
16
|
+
invalid_keys: invalid_keys,
|
17
|
+
details: details
|
18
|
+
}
|
19
|
+
|
20
|
+
super(error_data.to_json)
|
16
21
|
end
|
17
22
|
end
|
18
23
|
end
|
@@ -8,11 +8,15 @@ module Rager
|
|
8
8
|
class ParseError < Rager::Error
|
9
9
|
extend T::Sig
|
10
10
|
|
11
|
-
sig { params(
|
12
|
-
def initialize(
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
sig { params(body: String, details: T.nilable(String)).void }
|
12
|
+
def initialize(body, details: nil)
|
13
|
+
error_data = {
|
14
|
+
type: "parse",
|
15
|
+
body: body,
|
16
|
+
details: details
|
17
|
+
}
|
18
|
+
|
19
|
+
super(error_data.to_json)
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
@@ -8,10 +8,16 @@ module Rager
|
|
8
8
|
class TemplateError < Rager::Error
|
9
9
|
extend T::Sig
|
10
10
|
|
11
|
-
sig { params(
|
12
|
-
def initialize(
|
13
|
-
|
14
|
-
|
11
|
+
sig { params(template: String, variables: T::Hash[Symbol, T.untyped], details: T.nilable(String)).void }
|
12
|
+
def initialize(template, variables, details: nil)
|
13
|
+
error_data = {
|
14
|
+
type: "template",
|
15
|
+
template: template,
|
16
|
+
variables: variables,
|
17
|
+
details: details
|
18
|
+
}
|
19
|
+
|
20
|
+
super(error_data.to_json)
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
6
|
+
module Rager
|
7
|
+
module Errors
|
8
|
+
class TimeoutError < Rager::Error
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { params(operation: String, timeout_seconds: T.nilable(Numeric), attempts: T.nilable(Integer), details: T.nilable(String)).void }
|
12
|
+
def initialize(operation, timeout_seconds: nil, attempts: nil, details: nil)
|
13
|
+
error_data = {
|
14
|
+
type: "timeout",
|
15
|
+
operation: operation,
|
16
|
+
timeout_seconds: timeout_seconds,
|
17
|
+
attempts: attempts,
|
18
|
+
details: details
|
19
|
+
}
|
20
|
+
|
21
|
+
super(error_data.to_json)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -11,7 +11,11 @@ module Rager
|
|
11
11
|
|
12
12
|
sig { void }
|
13
13
|
def initialize
|
14
|
-
|
14
|
+
begin
|
15
|
+
require "async/http"
|
16
|
+
rescue LoadError
|
17
|
+
raise Rager::Errors::DependencyError.new("async-http", details: "Please install the async-http gem to use the AsyncHttp adapter")
|
18
|
+
end
|
15
19
|
|
16
20
|
@internet = T.let(Async::HTTP::Internet.new, Async::HTTP::Internet)
|
17
21
|
end
|
@@ -22,22 +26,21 @@ module Rager
|
|
22
26
|
).returns(Rager::Http::Response)
|
23
27
|
}
|
24
28
|
def make_request(request)
|
25
|
-
response =
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
response = wrap_if_timeout(request.timeout) do
|
30
|
+
@internet.call(
|
31
|
+
request.verb.serialize,
|
32
|
+
request.url,
|
33
|
+
request.headers.to_a,
|
34
|
+
request.body
|
35
|
+
)
|
36
|
+
end
|
31
37
|
|
32
38
|
body = if response.body.nil?
|
33
39
|
nil
|
34
|
-
elsif
|
35
|
-
response.headers["content-type"]&.downcase&.include?("text/event-stream")
|
40
|
+
elsif request.streaming
|
36
41
|
body_enum(response)
|
37
42
|
else
|
38
|
-
|
39
|
-
response.body.each { |chunk| body_parts << chunk }
|
40
|
-
body_parts.join.force_encoding("UTF-8")
|
43
|
+
response.body.join
|
41
44
|
end
|
42
45
|
|
43
46
|
Response.new(
|
@@ -45,10 +48,61 @@ module Rager
|
|
45
48
|
headers: response.headers.to_h,
|
46
49
|
body: body
|
47
50
|
)
|
51
|
+
rescue SocketError => e
|
52
|
+
raise Rager::Errors::HttpError.new(
|
53
|
+
self,
|
54
|
+
request.url,
|
55
|
+
0,
|
56
|
+
body: nil,
|
57
|
+
details: "DNS resolution failed: #{e.message}"
|
58
|
+
)
|
59
|
+
rescue Errno::ECONNREFUSED => e
|
60
|
+
raise Rager::Errors::HttpError.new(
|
61
|
+
self,
|
62
|
+
request.url,
|
63
|
+
0,
|
64
|
+
body: nil,
|
65
|
+
details: "Connection refused: #{e.message}"
|
66
|
+
)
|
67
|
+
rescue Errno::ETIMEDOUT => e
|
68
|
+
raise Rager::Errors::HttpError.new(
|
69
|
+
self,
|
70
|
+
request.url,
|
71
|
+
0,
|
72
|
+
body: nil,
|
73
|
+
details: "Connection timed out: #{e.message}"
|
74
|
+
)
|
75
|
+
rescue Async::TimeoutError => e
|
76
|
+
raise Rager::Errors::HttpError.new(
|
77
|
+
self,
|
78
|
+
request.url,
|
79
|
+
0,
|
80
|
+
body: nil,
|
81
|
+
details: "Request timed out: #{e.message}"
|
82
|
+
)
|
83
|
+
rescue EOFError => e
|
84
|
+
raise Rager::Errors::HttpError.new(
|
85
|
+
self,
|
86
|
+
request.url,
|
87
|
+
0,
|
88
|
+
body: nil,
|
89
|
+
details: "Connection closed unexpectedly: #{e.message}"
|
90
|
+
)
|
48
91
|
end
|
49
92
|
|
50
93
|
private
|
51
94
|
|
95
|
+
sig { params(timeout: T.nilable(Numeric), block: T.proc.returns(T.untyped)).returns(T.untyped) }
|
96
|
+
def wrap_if_timeout(timeout, &block)
|
97
|
+
if timeout
|
98
|
+
Async::Task.current.with_timeout(timeout) do
|
99
|
+
block.call
|
100
|
+
end
|
101
|
+
else
|
102
|
+
block.call
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
52
106
|
sig {
|
53
107
|
params(
|
54
108
|
response: Async::HTTP::Protocol::Response
|
@@ -56,7 +110,7 @@ module Rager
|
|
56
110
|
}
|
57
111
|
def body_enum(response)
|
58
112
|
Enumerator.new do |yielder|
|
59
|
-
response.body.each { |chunk| yielder << chunk
|
113
|
+
response.body.each { |chunk| yielder << chunk }
|
60
114
|
ensure
|
61
115
|
response.close
|
62
116
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "base64"
|
4
5
|
require "fileutils"
|
5
6
|
require "json"
|
7
|
+
|
6
8
|
require "sorbet-runtime"
|
7
9
|
|
8
10
|
module Rager
|
@@ -20,13 +22,9 @@ module Rager
|
|
20
22
|
chunk_delimiter: T.nilable(String)
|
21
23
|
).void
|
22
24
|
end
|
23
|
-
def initialize(
|
24
|
-
test_file_path,
|
25
|
-
fallback_adapter = nil,
|
26
|
-
chunk_delimiter = nil
|
27
|
-
)
|
25
|
+
def initialize(test_file_path, fallback_adapter = nil, chunk_delimiter = nil)
|
28
26
|
@test_file_path = T.let(test_file_path, String)
|
29
|
-
@fallback_adapter = T.let(fallback_adapter || Rager::Http::Adapters::
|
27
|
+
@fallback_adapter = T.let(fallback_adapter || Rager::Http::Adapters::NetHttp.new, Rager::Http::Adapters::Abstract)
|
30
28
|
@cache = T.let(load_cache, Cache)
|
31
29
|
end
|
32
30
|
|
@@ -34,48 +32,50 @@ module Rager
|
|
34
32
|
def make_request(request)
|
35
33
|
key = request.serialize.to_json
|
36
34
|
cached_entry = @cache[key]
|
37
|
-
|
38
|
-
build_response_from_cache(cached_entry)
|
39
|
-
else
|
40
|
-
fetch_and_cache_response(request, key)
|
41
|
-
end
|
35
|
+
cached_entry ? build_response_from_cache(cached_entry) : fetch_and_cache_response(request, key)
|
42
36
|
end
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
key: String
|
48
|
-
).returns(Rager::Http::Response)
|
49
|
-
}
|
38
|
+
private
|
39
|
+
|
40
|
+
sig { params(request: Rager::Http::Request, key: String).returns(Rager::Http::Response) }
|
50
41
|
def fetch_and_cache_response(request, key)
|
51
42
|
response = @fallback_adapter.make_request(request)
|
52
43
|
|
53
|
-
serialized_response =
|
44
|
+
serialized_response = {
|
54
45
|
"status" => response.status,
|
55
46
|
"headers" => response.headers
|
56
|
-
}
|
47
|
+
}
|
57
48
|
|
58
|
-
if response.body.is_a?(Enumerator)
|
59
|
-
|
49
|
+
if request.streaming && response.body.is_a?(Enumerator)
|
50
|
+
raw_chunks = T.let([], T::Array[String])
|
51
|
+
T.cast(response.body, T::Enumerator[String]).each { |chunk| raw_chunks << chunk }
|
60
52
|
|
61
|
-
|
62
|
-
|
63
|
-
end
|
53
|
+
has_binary = raw_chunks.any? { |chunk| binary_content?(chunk) }
|
54
|
+
chunks = raw_chunks.map { |chunk| binary_content?(chunk) ? Base64.strict_encode64(chunk) : chunk }
|
64
55
|
|
65
56
|
serialized_response["body"] = chunks
|
66
57
|
serialized_response["is_stream"] = true
|
58
|
+
serialized_response["is_binary"] = has_binary
|
67
59
|
|
68
60
|
response_body = Enumerator.new do |yielder|
|
69
|
-
|
61
|
+
raw_chunks.each { |chunk| yielder << chunk }
|
70
62
|
end
|
71
63
|
else
|
72
|
-
|
64
|
+
body = T.cast(response.body, T.nilable(String)) || ""
|
65
|
+
|
66
|
+
if binary_content?(body)
|
67
|
+
serialized_response["body"] = Base64.strict_encode64(body)
|
68
|
+
serialized_response["is_binary"] = true
|
69
|
+
else
|
70
|
+
serialized_response["body"] = body
|
71
|
+
serialized_response["is_binary"] = false
|
72
|
+
end
|
73
|
+
|
73
74
|
serialized_response["is_stream"] = false
|
74
|
-
response_body =
|
75
|
+
response_body = body
|
75
76
|
end
|
76
77
|
|
77
78
|
@cache[key] = serialized_response
|
78
|
-
|
79
79
|
save_cache
|
80
80
|
|
81
81
|
Rager::Http::Response.new(
|
@@ -85,19 +85,19 @@ module Rager
|
|
85
85
|
)
|
86
86
|
end
|
87
87
|
|
88
|
-
sig {
|
89
|
-
params(
|
90
|
-
entry: T::Hash[String, T.untyped]
|
91
|
-
).returns(Rager::Http::Response)
|
92
|
-
}
|
88
|
+
sig { params(entry: T::Hash[String, T.untyped]).returns(Rager::Http::Response) }
|
93
89
|
def build_response_from_cache(entry)
|
94
90
|
body = entry["body"]
|
95
91
|
is_stream = entry["is_stream"] || false
|
92
|
+
is_binary = entry["is_binary"] || false
|
96
93
|
|
97
94
|
body = if is_stream
|
98
|
-
T.cast(body, T::Array[String])
|
95
|
+
chunks = T.cast(body, T::Array[String])
|
96
|
+
Enumerator.new do |yielder|
|
97
|
+
chunks.each { |chunk| yielder << (is_binary ? Base64.strict_decode64(chunk) : chunk) }
|
98
|
+
end
|
99
99
|
else
|
100
|
-
body
|
100
|
+
is_binary ? Base64.strict_decode64(T.cast(body, String)) : body
|
101
101
|
end
|
102
102
|
|
103
103
|
Rager::Http::Response.new(
|
@@ -116,21 +116,19 @@ module Rager
|
|
116
116
|
sig { returns(Cache) }
|
117
117
|
def load_cache
|
118
118
|
create_file_if_not_exists
|
119
|
-
JSON.parse(File.read(@test_file_path
|
119
|
+
JSON.parse(File.read(@test_file_path))
|
120
120
|
end
|
121
121
|
|
122
122
|
sig { void }
|
123
123
|
def save_cache
|
124
|
-
current_file_content =
|
125
|
-
if File.exist?(@test_file_path)
|
126
|
-
JSON.parse(File.read(@test_file_path, encoding: "UTF-8"))
|
127
|
-
else
|
128
|
-
{}
|
129
|
-
end
|
130
|
-
|
124
|
+
current_file_content = File.exist?(@test_file_path) ? JSON.parse(File.read(@test_file_path)) : {}
|
131
125
|
output = current_file_content.merge(@cache)
|
132
|
-
|
133
|
-
|
126
|
+
File.write(@test_file_path, JSON.generate(output))
|
127
|
+
end
|
128
|
+
|
129
|
+
sig { params(content: String).returns(T::Boolean) }
|
130
|
+
def binary_content?(content)
|
131
|
+
content.include?("\0")
|
134
132
|
end
|
135
133
|
end
|
136
134
|
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "net/http"
|
5
|
+
require "uri"
|
6
|
+
require "sorbet-runtime"
|
7
|
+
|
8
|
+
module Rager
|
9
|
+
module Http
|
10
|
+
module Adapters
|
11
|
+
class NetHttp < Rager::Http::Adapters::Abstract
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig { void }
|
15
|
+
def initialize
|
16
|
+
end
|
17
|
+
|
18
|
+
sig {
|
19
|
+
override.params(
|
20
|
+
request: Rager::Http::Request
|
21
|
+
).returns(Rager::Http::Response)
|
22
|
+
}
|
23
|
+
def make_request(request)
|
24
|
+
uri = URI(request.url)
|
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
|
31
|
+
|
32
|
+
http_request = build_http_request(request, uri)
|
33
|
+
|
34
|
+
http.request(http_request) do |response|
|
35
|
+
body = if request.streaming
|
36
|
+
body_enum(response)
|
37
|
+
else
|
38
|
+
response.body
|
39
|
+
end
|
40
|
+
|
41
|
+
return Rager::Http::Response.new(
|
42
|
+
status: response.code.to_i,
|
43
|
+
headers: response.to_hash,
|
44
|
+
body: body
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
rescue SocketError => e
|
49
|
+
raise Rager::Errors::HttpError.new(
|
50
|
+
self,
|
51
|
+
request.url,
|
52
|
+
0,
|
53
|
+
body: nil,
|
54
|
+
details: "DNS resolution failed: #{e.message}"
|
55
|
+
)
|
56
|
+
rescue Errno::ECONNREFUSED => e
|
57
|
+
raise Rager::Errors::HttpError.new(
|
58
|
+
self,
|
59
|
+
request.url,
|
60
|
+
0,
|
61
|
+
body: nil,
|
62
|
+
details: "Connection refused: #{e.message}"
|
63
|
+
)
|
64
|
+
rescue Errno::EBUSY => e
|
65
|
+
raise Rager::Errors::HttpError.new(
|
66
|
+
self,
|
67
|
+
request.url,
|
68
|
+
0,
|
69
|
+
body: nil,
|
70
|
+
details: "Device or resource busy: #{e.message}"
|
71
|
+
)
|
72
|
+
rescue Net::OpenTimeout => e
|
73
|
+
raise Rager::Errors::HttpError.new(
|
74
|
+
self,
|
75
|
+
request.url,
|
76
|
+
0,
|
77
|
+
body: nil,
|
78
|
+
details: "Connection timed out: #{e.message}"
|
79
|
+
)
|
80
|
+
rescue Net::ReadTimeout => e
|
81
|
+
raise Rager::Errors::HttpError.new(
|
82
|
+
self,
|
83
|
+
request.url,
|
84
|
+
0,
|
85
|
+
body: nil,
|
86
|
+
details: "Read timed out: #{e.message}"
|
87
|
+
)
|
88
|
+
rescue EOFError => e
|
89
|
+
raise Rager::Errors::HttpError.new(
|
90
|
+
self,
|
91
|
+
request.url,
|
92
|
+
0,
|
93
|
+
body: nil,
|
94
|
+
details: "Connection closed unexpectedly: #{e.message}"
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
sig {
|
101
|
+
params(
|
102
|
+
response: Net::HTTPResponse
|
103
|
+
).returns(T::Enumerator[String])
|
104
|
+
}
|
105
|
+
def body_enum(response)
|
106
|
+
chunks = T.let([], T::Array[String])
|
107
|
+
|
108
|
+
response.read_body do |chunk|
|
109
|
+
chunks << chunk
|
110
|
+
end
|
111
|
+
|
112
|
+
chunks.to_enum
|
113
|
+
end
|
114
|
+
|
115
|
+
sig {
|
116
|
+
params(
|
117
|
+
request: Rager::Http::Request,
|
118
|
+
uri: URI::Generic
|
119
|
+
).returns(Net::HTTPRequest)
|
120
|
+
}
|
121
|
+
def build_http_request(request, uri)
|
122
|
+
http_request = case request.verb
|
123
|
+
when Rager::Http::Verb::Get then Net::HTTP::Get.new(uri)
|
124
|
+
when Rager::Http::Verb::Post then Net::HTTP::Post.new(uri)
|
125
|
+
when Rager::Http::Verb::Put then Net::HTTP::Put.new(uri)
|
126
|
+
when Rager::Http::Verb::Patch then Net::HTTP::Patch.new(uri)
|
127
|
+
when Rager::Http::Verb::Delete then Net::HTTP::Delete.new(uri)
|
128
|
+
when Rager::Http::Verb::Head then Net::HTTP::Head.new(uri)
|
129
|
+
when Rager::Http::Verb::Options then Net::HTTP::Options.new(uri)
|
130
|
+
end
|
131
|
+
|
132
|
+
request.headers.each do |key, value|
|
133
|
+
http_request[key] = value
|
134
|
+
end
|
135
|
+
|
136
|
+
if request.body && http_request.request_body_permitted?
|
137
|
+
http_request.body = request.body
|
138
|
+
end
|
139
|
+
|
140
|
+
http_request
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/lib/rager/http/request.rb
CHANGED
@@ -10,6 +10,8 @@ module Rager
|
|
10
10
|
const :verb, Rager::Http::Verb, default: Rager::Http::Verb::Get
|
11
11
|
const :headers, T::Hash[String, String]
|
12
12
|
const :body, T.nilable(String)
|
13
|
+
const :streaming, T::Boolean, default: false
|
14
|
+
const :timeout, T.nilable(Numeric)
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
@@ -13,7 +13,7 @@ module Rager
|
|
13
13
|
sig { override.params(prompt: String, options: Rager::ImageGen::Options).returns(Rager::Types::ImageGenOutput) }
|
14
14
|
def image_gen(prompt, options)
|
15
15
|
api_key = options.api_key || ENV["REPLICATE_API_KEY"]
|
16
|
-
raise Rager::Errors::
|
16
|
+
raise Rager::Errors::CredentialsError.new("Replicate", env_var: ["REPLICATE_API_KEY"]) if api_key.nil?
|
17
17
|
|
18
18
|
body = {
|
19
19
|
input: {
|
@@ -32,18 +32,19 @@ module Rager
|
|
32
32
|
"Content-Type" => "application/json",
|
33
33
|
"Prefer" => "wait"
|
34
34
|
},
|
35
|
-
body: body.to_json
|
35
|
+
body: body.to_json,
|
36
|
+
timeout: options.timeout || Rager.config.timeout
|
36
37
|
)
|
37
38
|
|
38
39
|
response = Rager.config.http_adapter.make_request(request)
|
39
40
|
response_body = T.cast(T.must(response.body), String)
|
40
41
|
|
41
|
-
raise Rager::Errors::HttpError.new(Rager.config.http_adapter, response.status, response_body) unless [200, 201].include?(response.status)
|
42
|
+
raise Rager::Errors::HttpError.new(Rager.config.http_adapter, request.url, response.status, body: response_body) unless [200, 201].include?(response.status)
|
42
43
|
|
43
44
|
parsed = JSON.parse(response_body)
|
44
45
|
parsed.fetch("output").first
|
45
46
|
rescue JSON::ParserError, KeyError => e
|
46
|
-
raise Rager::Errors::ParseError.new(e.message
|
47
|
+
raise Rager::Errors::ParseError.new(response_body || "", details: e.message)
|
47
48
|
end
|
48
49
|
end
|
49
50
|
end
|