activeclient_api 0.1.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 +7 -0
- data/README.md +83 -0
- data/Rakefile +3 -0
- data/lib/active_client/base.rb +118 -0
- data/lib/active_client/log_subscriber.rb +34 -0
- data/lib/active_client/railtie.rb +4 -0
- data/lib/active_client/version.rb +3 -0
- data/lib/active_client.rb +9 -0
- data/lib/activeclient_api.rb +1 -0
- metadata +62 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1e4355874cf52251d674000e9d6b6affa02deed86dd42a426d202262792cb1ab
|
4
|
+
data.tar.gz: dfd3243aff05f261a33eafe79fe06cf19c29de640d0501d790f3143554599eb5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5b7e5fb4b6a5d7ca31e2259df22b4e9bc6a5f29a25bdfc5c0b0396b431caa57b3fadaa9e8dc585705e8d066bf126ec4debf00c026f9db9276c8aa983fc9a7763
|
7
|
+
data.tar.gz: 2bddd36d9c27340fa490c60b96fa85bbf6a27feecea15e443260fb34541dc101942f2df0fbfaef67ca437cf31487014e6acba643502f977759d737de7a825be8
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# ActiveClient
|
2
|
+
|
3
|
+
Basic API client interface
|
4
|
+
|
5
|
+
## Example OpenAI client
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class OpenAI < ActiveClient::Base
|
9
|
+
class BadRequestError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(model = "gpt-4o-mini-2024-07-18")
|
13
|
+
@model = model
|
14
|
+
super()
|
15
|
+
end
|
16
|
+
|
17
|
+
def response(input, name, schema, tools = [])
|
18
|
+
response = post "responses", **request_params(input, name, schema, tools)
|
19
|
+
|
20
|
+
raise BadRequestError, response unless response.success?
|
21
|
+
|
22
|
+
JSON.parse(response.body.to_h["output"].
|
23
|
+
find { it["type"] == "message" }.dig("content", 0, "text"))
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_vector_store(name, *file_ids)
|
27
|
+
post("vector_stores", name:, file_ids:,
|
28
|
+
expires_after: { days: 1, anchor: :last_active_at })
|
29
|
+
end
|
30
|
+
|
31
|
+
def vector_store(id)
|
32
|
+
get("vector_stores/#{id}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def vector_stores
|
36
|
+
get("vector_stores")
|
37
|
+
end
|
38
|
+
|
39
|
+
def files
|
40
|
+
get("files")
|
41
|
+
end
|
42
|
+
|
43
|
+
def upload_file(name, content)
|
44
|
+
multipart_post("files", [%w(purpose user_data),
|
45
|
+
["file", StringIO.new(content),
|
46
|
+
{ filename: name, content_type: "text/plain" }]])
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_reader :model
|
52
|
+
|
53
|
+
def default_headers = super.merge("Authorization" => token_header)
|
54
|
+
def base_url = "https://api.openai.com/v1"
|
55
|
+
def token_header = "Bearer #{credentials.dig(:open_ai, :token)}"
|
56
|
+
|
57
|
+
def request_params(input, name, schema, tools)
|
58
|
+
{ input:, model:, store: false, top_p: 1.0, tools:, text: { format: {
|
59
|
+
type: :json_schema, name:, schema:, strict: true
|
60
|
+
} } }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
## Installation
|
66
|
+
Add this line to your application's Gemfile:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
gem "activeclient_api"
|
70
|
+
```
|
71
|
+
|
72
|
+
And then execute:
|
73
|
+
```bash
|
74
|
+
$ bundle
|
75
|
+
```
|
76
|
+
|
77
|
+
Or install it yourself as:
|
78
|
+
```bash
|
79
|
+
$ gem install activeclient_api
|
80
|
+
```
|
81
|
+
|
82
|
+
## License
|
83
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
class ActiveClient::Base
|
2
|
+
Response = Struct.new(:body, :success?)
|
3
|
+
|
4
|
+
def get(path, skip_parsing: false, **query)
|
5
|
+
make_request(Net::HTTP::Get, path, skip_parsing:, query:)
|
6
|
+
end
|
7
|
+
|
8
|
+
def post(path, **body)
|
9
|
+
make_request(Net::HTTP::Post, path, body:)
|
10
|
+
end
|
11
|
+
|
12
|
+
def delete(path)
|
13
|
+
make_request(Net::HTTP::Delete, path)
|
14
|
+
end
|
15
|
+
|
16
|
+
def multipart_post(path, body, skip_parsing: false)
|
17
|
+
instrument(klass: Net::HTTP::Post, path:, query: nil,
|
18
|
+
body: nil) do |http, request|
|
19
|
+
request.set_form(body, "multipart/form-data")
|
20
|
+
|
21
|
+
response = http.request(request)
|
22
|
+
|
23
|
+
Response.new(parse_response(response.body, skip_parsing),
|
24
|
+
response.is_a?(Net::HTTPSuccess))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def instrument(klass:, path:, query:, body:)
|
31
|
+
uri, http, request = construct_request(klass:, path:, query:)
|
32
|
+
request.body = default_body.merge(body).to_json if body.present?
|
33
|
+
loggable = loggable_uri(uri)
|
34
|
+
args = { name: self.class.name.demodulize, uri: loggable }
|
35
|
+
|
36
|
+
ActiveSupport::Notifications.
|
37
|
+
instrument("request.active_client", args) { yield http, request }
|
38
|
+
end
|
39
|
+
|
40
|
+
def make_request(klass, path, skip_parsing: false, query: {}, body: {})
|
41
|
+
instrument(klass:, path:, query:, body:) do |http, request|
|
42
|
+
response = http.request(request)
|
43
|
+
|
44
|
+
Response.new(parse_response(response.body, skip_parsing),
|
45
|
+
response.is_a?(Net::HTTPSuccess))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_response(body, skip_parsing)
|
50
|
+
if skip_parsing
|
51
|
+
body
|
52
|
+
else
|
53
|
+
JSON.parse(body)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def construct_request(klass:, path:, query:)
|
58
|
+
uri = construct_uri(path:, query:)
|
59
|
+
|
60
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
61
|
+
http.use_ssl = uri.instance_of?(URI::HTTPS)
|
62
|
+
http.read_timeout = 1200
|
63
|
+
|
64
|
+
[uri, http, klass.new(uri.request_uri, default_headers)]
|
65
|
+
end
|
66
|
+
|
67
|
+
def construct_uri(path:, query:)
|
68
|
+
if base_url.present?
|
69
|
+
URI(URI::DEFAULT_PARSER.escape(File.join(base_url, path)))
|
70
|
+
else
|
71
|
+
URI(URI::DEFAULT_PARSER.escape(path))
|
72
|
+
end.tap { |uri| build_query(uri, query) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def build_query(uri, query)
|
76
|
+
return unless query.present? || default_query.present?
|
77
|
+
|
78
|
+
uri.query = URI.encode_www_form(default_query.merge(query))
|
79
|
+
end
|
80
|
+
|
81
|
+
def default_headers
|
82
|
+
{ "Accept" => "application/json",
|
83
|
+
"Content-Type" => "application/json" }
|
84
|
+
end
|
85
|
+
|
86
|
+
def default_query
|
87
|
+
{}
|
88
|
+
end
|
89
|
+
|
90
|
+
def default_body
|
91
|
+
{}
|
92
|
+
end
|
93
|
+
|
94
|
+
def base_url
|
95
|
+
end
|
96
|
+
|
97
|
+
def token_header
|
98
|
+
raise NotImplementedError
|
99
|
+
end
|
100
|
+
|
101
|
+
def credentials
|
102
|
+
Rails.application.credentials
|
103
|
+
end
|
104
|
+
|
105
|
+
def all_credentials
|
106
|
+
credentials.to_h.values.flat_map do |cred|
|
107
|
+
cred.try(:values).presence || cred
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def loggable_uri(uri)
|
112
|
+
uri.to_s.tap do |loggable|
|
113
|
+
all_credentials.each do |s|
|
114
|
+
loggable.gsub!(s, "[FILTERED]")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "active_support/version"
|
2
|
+
require "active_support/deprecation"
|
3
|
+
require "active_support/deprecator"
|
4
|
+
require "active_support/log_subscriber"
|
5
|
+
require "active_support/isolated_execution_state"
|
6
|
+
|
7
|
+
module ActiveClient
|
8
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
9
|
+
def self.runtime=(value)
|
10
|
+
Thread.current[:active_client_runtime] = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.runtime
|
14
|
+
Thread.current[:active_client_runtime] ||= 0
|
15
|
+
end
|
16
|
+
|
17
|
+
# :nocov:
|
18
|
+
def self.reset_runtime
|
19
|
+
rt, self.runtime = runtime, 0
|
20
|
+
rt
|
21
|
+
end
|
22
|
+
# :nocov:
|
23
|
+
|
24
|
+
def request(event)
|
25
|
+
self.class.runtime += event.duration
|
26
|
+
return unless logger.debug?
|
27
|
+
|
28
|
+
log_name = color("#{event.payload[:name]} (#{event.duration.round(1)}ms)",
|
29
|
+
YELLOW, bold: true)
|
30
|
+
|
31
|
+
debug " #{log_name} #{color(event.payload[:uri], YELLOW, bold: true)}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "active_client"
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activeclient_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Pezza
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-03-22 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: rails
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '8.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '8.0'
|
26
|
+
email:
|
27
|
+
- pezza@hey.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- README.md
|
33
|
+
- Rakefile
|
34
|
+
- lib/active_client.rb
|
35
|
+
- lib/active_client/base.rb
|
36
|
+
- lib/active_client/log_subscriber.rb
|
37
|
+
- lib/active_client/railtie.rb
|
38
|
+
- lib/active_client/version.rb
|
39
|
+
- lib/activeclient_api.rb
|
40
|
+
homepage: https://github.com/npezza93/activeclient
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata:
|
44
|
+
rubygems_mfa_required: 'true'
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 3.4.0
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubygems_version: 3.6.2
|
60
|
+
specification_version: 4
|
61
|
+
summary: Basic client for make api classes
|
62
|
+
test_files: []
|