chroma-db 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8334143128edb78d734a0154da3432e1d8e222922341b182e1d589f91a227a3
4
- data.tar.gz: 30f9c52759742ca2848e87df649c683a683bce9a00041abb9ed91a77b746dd9a
3
+ metadata.gz: 9273343cf2f29991d61d8e5393dfd06541211c84e03769162fa984c9ca2c8620
4
+ data.tar.gz: 3dd3e94d5914e5776aef673f18b06bbb6f61254bbe4eb51ae25f8675f6c9c72b
5
5
  SHA512:
6
- metadata.gz: 41838e89fe84ef6317092a8fc604ae13ec99a2619fbb56fb18b72d2b5b837dd2fa550ebdb0e9802b6026ba487e250d5839d0417e59bcb99589137debdbec82ef
7
- data.tar.gz: 2dfd54367af2f122265a51e1e9d2e228e58b14984f4aab91e940f732f682db7ea5744fb758bb114875165b5ca907be6493d6aa3d1b68637ff79d65c07b7f62b4
6
+ metadata.gz: 8c94d838879f22cae956b9a4b7e66f8b8a8d76792770dda6eb9a91b197aa0eb3b8408256653402feb750732f35fb221ae2ebedfe05ab49d89896b80579228ed0
7
+ data.tar.gz: f5ad7421b54da9909a3a1c76d36d07738ccd44aaa934a7e960e5b102608e24c61e36243b59c0d2ba469e01c34b2cc360e2a07a82516d1a4a2d73cfffa49334fa
data/Gemfile CHANGED
@@ -12,3 +12,5 @@ gem "minitest", "~> 5.0"
12
12
  gem "standard", "~> 1.3"
13
13
 
14
14
  gem "rbs", "~> 3.1"
15
+
16
+ gem "webmock", "~> 3.18", ">= 3.18.1"
data/Gemfile.lock CHANGED
@@ -1,14 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- chroma-db (0.1.0)
4
+ chroma-db (0.2.0)
5
5
  dry-monads (~> 1.6)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ addressable (2.8.4)
11
+ public_suffix (>= 2.0.2, < 6.0)
10
12
  ast (2.4.2)
11
13
  concurrent-ruby (1.2.2)
14
+ crack (0.4.5)
15
+ rexml
12
16
  dry-core (1.0.0)
13
17
  concurrent-ruby (~> 1.0)
14
18
  zeitwerk (~> 2.6)
@@ -16,6 +20,7 @@ GEM
16
20
  concurrent-ruby (~> 1.0)
17
21
  dry-core (~> 1.0, < 2)
18
22
  zeitwerk (~> 2.6)
23
+ hashdiff (1.0.1)
19
24
  json (2.6.3)
20
25
  language_server-protocol (3.17.0.3)
21
26
  lint_roller (1.0.0)
@@ -23,6 +28,7 @@ GEM
23
28
  parallel (1.23.0)
24
29
  parser (3.2.2.1)
25
30
  ast (~> 2.4.1)
31
+ public_suffix (5.0.1)
26
32
  rainbow (3.1.1)
27
33
  rake (13.0.6)
28
34
  rbs (3.1.0)
@@ -56,10 +62,15 @@ GEM
56
62
  lint_roller (~> 1.0)
57
63
  rubocop-performance (~> 1.16.0)
58
64
  unicode-display_width (2.4.2)
65
+ webmock (3.18.1)
66
+ addressable (>= 2.8.0)
67
+ crack (>= 0.3.2)
68
+ hashdiff (>= 0.4.0, < 2.0.0)
59
69
  zeitwerk (2.6.8)
60
70
 
61
71
  PLATFORMS
62
72
  arm64-darwin-22
73
+ x86_64-linux
63
74
 
64
75
  DEPENDENCIES
65
76
  chroma-db!
@@ -67,6 +78,7 @@ DEPENDENCIES
67
78
  rake (~> 13.0)
68
79
  rbs (~> 3.1)
69
80
  standard (~> 1.3)
81
+ webmock (~> 3.18, >= 3.18.1)
70
82
 
71
83
  BUNDLED WITH
72
84
  2.4.12
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chroma
4
+ module APIOperations
5
+ # Request's response Data object.
6
+ #
7
+ # status - HTTP status code. It is zero when a request fails due to network error.
8
+ # body - Parsed JSON object or response body.
9
+ # headers - HTTP response headers.
10
+ # error - Exception or Net::HTTPResponse object if the response is not Net::HTTPSuccess
11
+ Response = Data.define(:status, :body, :headers, :error)
12
+
13
+ # Request module provides functionality to perform HTTP requests.
14
+ module Request
15
+ module ClassMethods
16
+ include Dry::Monads[:result]
17
+
18
+ # Execute an HTTP request and return a monad wrapping the response.
19
+ #
20
+ # method - The HTTP method to use (e.g. 'GET', 'POST'). Method must be a `Symbol`.
21
+ # url - The URL to send the request to.
22
+ # params - The query parameters or request body. Params needs to be in a form of a Hash.
23
+ # options - Additional options to pass to the request.
24
+ #
25
+ # A `Dry::Monads::Result` monad wrapping the response, either a success or failure.
26
+ # The response is a `Chroma::APIOperations::Response` Data object.
27
+ #
28
+ # Examples
29
+ #
30
+ # result = execute_request(:get, "https://example.com", {name: "test request"})
31
+ # if result.success?
32
+ # puts "Response status: #{result.success.status}"
33
+ # puts "Response body: #{result.success.body}"
34
+ # else
35
+ # puts "Request failed with error: #{result.failure.error}"
36
+ # end
37
+ def execute_request(method, url, params = {}, options = {})
38
+ uri = URI.parse(url)
39
+
40
+ request = build_request(method, uri, params)
41
+
42
+ use_ssl = options.delete(:use_ssl) || false
43
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl:) do |http|
44
+ Chroma::Util.log_debug("Sending a request", {method:, uri:, params:})
45
+ http.request(request)
46
+ end
47
+
48
+ build_response(response)
49
+ rescue => ex
50
+ build_response(ex)
51
+ end
52
+
53
+ private def build_response(response)
54
+ case response
55
+ in Net::HTTPSuccess => success_response
56
+ Chroma::Util.log_info("Successful response", code: success_response.code)
57
+
58
+ build_response_details(success_response)
59
+ in Net::HTTPRedirection => redirect_response
60
+ Chroma::Util.log_info("Server redirect response", code: redirect_response.code, location: redirect_response["location"])
61
+
62
+ build_response_details(redirect_response)
63
+ in Net::HTTPClientError => client_error_response
64
+ Chroma::Util.log_error("Client error response", code: client_error_response.code, body: client_error_response.body)
65
+
66
+ build_response_details(client_error_response)
67
+ in Net::HTTPServerError => server_error_response
68
+ Chroma::Util.log_error("Server error response", code: server_error_response.code)
69
+
70
+ build_response_details(server_error_response, parse_body: false)
71
+ else
72
+ Chroma::Util.log_error("An error happened", error: response.to_s)
73
+
74
+ build_response_details(response, exception: true, parse_body: false)
75
+ end
76
+ end
77
+
78
+ private def build_response_details(response, exception: false, parse_body: true)
79
+ response_data = Chroma::APIOperations::Response.new(
80
+ status: exception ? 0 : response.code.to_i,
81
+ body: if exception
82
+ exception.to_s
83
+ else
84
+ (parse_body ? body_to_json(response.body) : response.body)
85
+ end,
86
+ headers: exception ? {} : response.each_header.to_h,
87
+ error: response.is_a?(Net::HTTPSuccess) ? nil : response
88
+ )
89
+
90
+ case response
91
+ in Net::HTTPSuccess
92
+ return Success(response_data)
93
+ else
94
+ return Failure(response_data)
95
+ end
96
+ end
97
+
98
+ private def body_to_json(content)
99
+ JSON.parse(content, symbolize_keys: true)
100
+ rescue JSON::ParserError, TypeError
101
+ content
102
+ end
103
+
104
+ private def build_request(method, uri, params)
105
+ request = case method
106
+ when :post then Net::HTTP::Post.new(uri)
107
+ when :put then Net::HTTP::Put.new(uri)
108
+ when :delete then Net::HTTP::Delete.new(uri)
109
+ else
110
+ Net::HTTP::Get.new(uri)
111
+ end
112
+
113
+ request.content_type = "application/json"
114
+ request.body = params.to_json if params.size > 0
115
+
116
+ request
117
+ end
118
+ end
119
+
120
+ def self.included(base)
121
+ base.extend(ClassMethods)
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chroma
4
+ # map to the same values as the standard library's logger
5
+ LEVEL_DEBUG = Logger::DEBUG
6
+ LEVEL_ERROR = Logger::ERROR
7
+ LEVEL_INFO = Logger::INFO
8
+
9
+ @config = Chroma::ChromaConfiguration.setup
10
+
11
+ class << self
12
+ extend Forwardable
13
+
14
+ attr_reader :config
15
+
16
+ # User configuration options
17
+ def_delegators :@config, :connect_host, :connect_host=
18
+ def_delegators :@config, :api_base, :api_base=
19
+ def_delegators :@config, :api_version, :api_version=
20
+ def_delegators :@config, :log_level, :log_level=
21
+ def_delegators :@config, :logger, :logger=
22
+ end
23
+
24
+ def self.api_url
25
+ "#{connect_host}/#{api_base}/#{api_version}"
26
+ end
27
+
28
+ Chroma.log_level = ENV["CHROMA_LOG"].to_i unless ENV["CHROMA_LOG"].nil?
29
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chroma
4
+ # The ChromaConfiguration class allows you to configure the connection settings for a Chroma service client.
5
+ # It can be initialized directly calling #new method.
6
+ #
7
+ # Examples
8
+ #
9
+ # configuration = Chroma::ChromaConfiguration.new
10
+ # configurarion.connect_host = "https://chroma.example.com"
11
+ #
12
+ # Or via a setup block.
13
+ #
14
+ # Examples
15
+ #
16
+ # Chroma::ChromaConfiguration.setup do |config|
17
+ # config.connect_host = "https://chroma.example.com"
18
+ # end
19
+ class ChromaConfiguration
20
+ # Sets the host name for the Chroma service.
21
+ #
22
+ # Examples
23
+ #
24
+ # config.connect_host = "chroma.example.com"
25
+ #
26
+ # Returns the String host name.
27
+ attr_accessor :connect_host
28
+
29
+ # Sets the base path for the Chroma API.
30
+ #
31
+ # Examples
32
+ #
33
+ # config.api_base = "/api"
34
+ #
35
+ # Returns the String API base path.
36
+ attr_accessor :api_base
37
+ # Sets the version of the Chroma API.
38
+ #
39
+ # Examples
40
+ #
41
+ # config.api_version = "v1"
42
+ #
43
+ # Returns the String API version.
44
+ attr_accessor :api_version
45
+ # Sets the logger for the Chroma service client.
46
+ #
47
+ # Examples
48
+ #
49
+ # config.logger = Logger.new(STDOUT)
50
+ #
51
+ # Returns the Logger instance.
52
+ attr_accessor :logger
53
+ # Sets the logger's log level for the Chroma service client.
54
+ #
55
+ # Examples
56
+ #
57
+ # config.log_level = Chroma::LEVEL_INFO
58
+ #
59
+ # Returns the log level constant
60
+ attr_accessor :log_level
61
+
62
+ def self.setup
63
+ new.tap do |instance|
64
+ yield(instance) if block_given?
65
+ end
66
+ end
67
+
68
+ def initialize
69
+ @api_base = "api"
70
+ @api_version = "v1"
71
+
72
+ @log_level = Chroma::LEVEL_INFO
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chroma
4
+ # ChromaError is the base error from which all other more specific Chrome
5
+ # errors derive.
6
+ class ChromaError < StandardError
7
+ attr_accessor :response
8
+
9
+ attr_reader :message
10
+ attr_reader :error
11
+ attr_reader :body
12
+ attr_reader :status
13
+
14
+ def initialize(message = nil, status: nil, body: nil, response: nil)
15
+ @message = message
16
+ @status = status
17
+ @body = body
18
+ @response = response
19
+ end
20
+
21
+ def to_s
22
+ status_value = @status.nil? ? "" : "(Status #{@status}) "
23
+ "#{status_value}#{@message}"
24
+ end
25
+ end
26
+
27
+ # APIConnectionError is raised in the event that the SDK can't connect to
28
+ # Chroma's database. That can be for a variety of different reasons from a
29
+ # downed network to a bad TLS certificate.
30
+ class APIConnectionError < ChromaError
31
+ end
32
+
33
+ # InvalidRequestError is raised when a request is initiated with invalid
34
+ # parameters.
35
+ class InvalidRequestError < ChromaError
36
+ def initialize(message = nil, status: nil, body: nil, response: nil)
37
+ super
38
+
39
+ decode_message
40
+ end
41
+
42
+ private
43
+
44
+ def decode_message
45
+ if message.include?("ValueError")
46
+ error = JSON.parse(message)
47
+ value = error.fetch("error", "").sub("ValueError('", "").sub("')", "")
48
+ @message = value if !value.nil?
49
+ end
50
+ rescue JSON::ParserError, TypeError
51
+ end
52
+ end
53
+
54
+ # APIError is a generic error that may be raised in cases where none of the
55
+ # other named errors cover the problem.
56
+ class APIError < ChromaError
57
+ def initialize(message = nil, status: nil, body: nil, response: nil)
58
+ super
59
+
60
+ decode_message
61
+ end
62
+
63
+ private
64
+
65
+ def decode_message
66
+ if message.is_a?(Hash) && message.has_key?("error")
67
+ @message = message.fetch("error")
68
+ end
69
+ end
70
+ end
71
+ end