chromadb-experimental 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: fae99389435684425baadb4d7c94dcd59b4ff2b1b304b157ce63c1a35b541fd8
4
- data.tar.gz: d231d2cdc468b5eb1301470e6025d032fde0743f7127a1b21cb679f7f717b2e1
3
+ metadata.gz: 2638e8c25b5a39193b9d3ec22a8196b9f093dd09a0bd39df754fd69d26969fa5
4
+ data.tar.gz: 14147058836b4dc4f1e5bf0fc1d763c756686bfe2fe604ce4341966ac78658fc
5
5
  SHA512:
6
- metadata.gz: 752dfdd6d4ef1b99bc8412910d6c8cf1add8f6fe346d91ffbfc53086b69186aa6d91caed18281b9e6b0f1662a6e308eb69fc53fc7bc05e6e8d4446561f8c0597
7
- data.tar.gz: 740a7ff83040ce0c8c8c7db33d4d568858d83a3c21113ec79b008cba7a8b7eb5c63ca1d1b3c12c1b4818395c3632b7685da9bc8ccc30fca973984813558bd086
6
+ metadata.gz: bddbe461695418288ae99b9f11d2d2d8fd64e054f8c897b28b89c1bdbfea3a4a121a62dff7412378e51e1cd4995d0a3680daa3a010eefaedb47caf8205845feb
7
+ data.tar.gz: 825d6d1999f39010968528ede4fa5144c32be4d7fb646a30b0b98c28ba7b2a0dee54d05c8cfd18dc797bf1465be5357ecee22b961cf1a594e03c6e65e98e06eb
@@ -293,18 +293,21 @@ module Chroma
293
293
  end
294
294
 
295
295
  class CloudClient < Client
296
- def initialize(cloud_host: "api.trychroma.com", cloud_port: 443, enable_ssl: true, headers: nil,
297
- tenant: nil, database: nil, api_key: nil, ssl_verify: true, timeout: nil)
298
- api_key ||= ENV["CHROMA_API_KEY"]
296
+ def initialize(api_key: nil, headers: nil, tenant: nil, database: nil, ssl_verify: true, timeout: nil)
297
+ config = Chroma.configuration
298
+ api_key ||= config.cloud_api_key
299
299
  raise ArgumentError, "CHROMA_API_KEY is required for CloudClient" if api_key.nil? || api_key.empty?
300
300
 
301
+ tenant ||= config.cloud_tenant
302
+ database ||= config.cloud_database
303
+
301
304
  merged_headers = (headers || {}).dup
302
305
  merged_headers["x-chroma-token"] ||= api_key
303
306
 
304
307
  super(
305
- host: cloud_host,
306
- port: cloud_port,
307
- ssl: enable_ssl,
308
+ host: config.cloud_host,
309
+ port: config.cloud_port,
310
+ ssl: config.cloud_ssl,
308
311
  headers: merged_headers,
309
312
  tenant: tenant,
310
313
  database: database,
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chroma
4
+ class ClientConfiguration
5
+ attr_accessor :cloud_api_key, :cloud_host, :cloud_port, :cloud_ssl, :cloud_tenant, :cloud_database
6
+
7
+ def initialize
8
+ @cloud_api_key = ENV["CHROMA_API_KEY"]
9
+ @cloud_host = ENV.fetch("CHROMA_CLOUD_HOST", "api.trychroma.com")
10
+ @cloud_port = normalize_port(ENV.fetch("CHROMA_CLOUD_PORT", "443"))
11
+ @cloud_ssl = ENV.fetch("CHROMA_CLOUD_SSL", "true").downcase != "false"
12
+ @cloud_tenant = ENV["CHROMA_TENANT"]
13
+ @cloud_database = ENV["CHROMA_DATABASE"]
14
+ end
15
+
16
+ private
17
+
18
+ def normalize_port(value)
19
+ Integer(value)
20
+ rescue ArgumentError, TypeError
21
+ 443
22
+ end
23
+ end
24
+
25
+ class << self
26
+ def configuration
27
+ @configuration ||= ClientConfiguration.new
28
+ end
29
+
30
+ def configure
31
+ yield(configuration) if block_given?
32
+ if defined?(Chroma::SharedState)
33
+ SharedState.register_cloud_api_key(configuration.cloud_api_key)
34
+ end
35
+ configuration
36
+ end
37
+
38
+ def reset_configuration!
39
+ @configuration = ClientConfiguration.new
40
+ if defined?(Chroma::SharedState)
41
+ SharedState.register_cloud_api_key(@configuration.cloud_api_key)
42
+ end
43
+ @configuration
44
+ end
45
+ end
46
+ end
@@ -176,7 +176,7 @@ module Chroma
176
176
  response = @client.transport.request(
177
177
  :post,
178
178
  "/tenants/#{path[:tenant]}/databases/#{path[:database]}/collections/#{path[:collection_id]}/fork",
179
- json: { "name" => name },
179
+ json: { "new_name" => name },
180
180
  )
181
181
  @client.send(:build_collection, response)
182
182
  end
@@ -489,6 +489,7 @@ module Chroma
489
489
  return Chroma::Search::Search.new(
490
490
  where: item[:where] || item["where"],
491
491
  rank: item[:rank] || item["rank"],
492
+ group_by: item[:group_by] || item["group_by"],
492
493
  limit: item[:limit] || item["limit"],
493
494
  select: item[:select] || item["select"],
494
495
  ).to_h
@@ -11,5 +11,5 @@ OpenAPI Generator version: 6.6.0
11
11
  =end
12
12
 
13
13
  module Chromadb
14
- VERSION = '0.1.0'
14
+ VERSION = '0.2.0'
15
15
  end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Chroma
6
+ module Search
7
+ module GroupByHelpers
8
+ module_function
9
+
10
+ def normalize_keys(keys, label)
11
+ raise ArgumentError, "#{label} cannot be empty" if keys.nil?
12
+
13
+ values = if keys.is_a?(Array) || keys.is_a?(Set)
14
+ keys.to_a
15
+ else
16
+ [ keys ]
17
+ end
18
+
19
+ raise ArgumentError, "#{label} cannot be empty" if values.empty?
20
+
21
+ values.map do |value|
22
+ normalized = value.respond_to?(:name) ? value.name : value
23
+ unless normalized.is_a?(String)
24
+ raise TypeError, "#{label} must be strings or Key instances"
25
+ end
26
+ normalized
27
+ end
28
+ end
29
+
30
+ def normalize_k(value, label)
31
+ unless value.is_a?(Integer) && value.positive?
32
+ raise TypeError, "#{label} must be a positive integer"
33
+ end
34
+ value
35
+ end
36
+
37
+ def fetch_value(hash, key)
38
+ hash[key] || hash[key.to_s]
39
+ end
40
+ end
41
+ private_constant :GroupByHelpers
42
+
43
+ class Aggregate
44
+ def to_h
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def self.from(input)
49
+ return input if input.is_a?(Aggregate)
50
+ unless input.is_a?(Hash)
51
+ raise TypeError, "Aggregate input must be an Aggregate or Hash"
52
+ end
53
+ raise ArgumentError, "Aggregate hash must contain exactly one operator" unless input.length == 1
54
+
55
+ operator, config = input.first
56
+ operator = operator.to_s
57
+
58
+ case operator
59
+ when "$min_k"
60
+ MinK.from_config(config)
61
+ when "$max_k"
62
+ MaxK.from_config(config)
63
+ else
64
+ raise ArgumentError, "Unknown aggregate operator: #{operator}"
65
+ end
66
+ end
67
+ end
68
+
69
+ class MinK < Aggregate
70
+ attr_reader :keys, :k
71
+
72
+ def initialize(keys:, k:)
73
+ @keys = GroupByHelpers.normalize_keys(keys, "MinK keys")
74
+ @k = GroupByHelpers.normalize_k(k, "MinK k")
75
+ end
76
+
77
+ def to_h
78
+ { "$min_k" => { "keys" => @keys.dup, "k" => @k } }
79
+ end
80
+
81
+ def self.from_config(config)
82
+ unless config.is_a?(Hash)
83
+ raise TypeError, "$min_k requires a Hash"
84
+ end
85
+
86
+ keys = GroupByHelpers.fetch_value(config, :keys)
87
+ k = GroupByHelpers.fetch_value(config, :k)
88
+ raise ArgumentError, "$min_k requires 'keys' field" if keys.nil?
89
+ raise ArgumentError, "$min_k requires 'k' field" if k.nil?
90
+
91
+ new(keys: keys, k: k)
92
+ end
93
+ end
94
+
95
+ class MaxK < Aggregate
96
+ attr_reader :keys, :k
97
+
98
+ def initialize(keys:, k:)
99
+ @keys = GroupByHelpers.normalize_keys(keys, "MaxK keys")
100
+ @k = GroupByHelpers.normalize_k(k, "MaxK k")
101
+ end
102
+
103
+ def to_h
104
+ { "$max_k" => { "keys" => @keys.dup, "k" => @k } }
105
+ end
106
+
107
+ def self.from_config(config)
108
+ unless config.is_a?(Hash)
109
+ raise TypeError, "$max_k requires a Hash"
110
+ end
111
+
112
+ keys = GroupByHelpers.fetch_value(config, :keys)
113
+ k = GroupByHelpers.fetch_value(config, :k)
114
+ raise ArgumentError, "$max_k requires 'keys' field" if keys.nil?
115
+ raise ArgumentError, "$max_k requires 'k' field" if k.nil?
116
+
117
+ new(keys: keys, k: k)
118
+ end
119
+ end
120
+
121
+ class GroupBy
122
+ attr_reader :keys, :aggregate
123
+
124
+ def initialize(keys: nil, aggregate: nil)
125
+ if keys.nil? && aggregate.nil?
126
+ @keys = []
127
+ @aggregate = nil
128
+ return
129
+ end
130
+
131
+ raise ArgumentError, "GroupBy requires 'keys' field" if keys.nil?
132
+ raise ArgumentError, "GroupBy requires 'aggregate' field" if aggregate.nil?
133
+
134
+ @keys = GroupByHelpers.normalize_keys(keys, "GroupBy keys")
135
+ @aggregate = Aggregate.from(aggregate)
136
+ end
137
+
138
+ def self.from(input)
139
+ return input if input.is_a?(GroupBy)
140
+ return nil if input.nil?
141
+ unless input.is_a?(Hash)
142
+ raise TypeError, "GroupBy input must be a GroupBy or Hash"
143
+ end
144
+
145
+ return GroupBy.new if input.empty?
146
+
147
+ keys = GroupByHelpers.fetch_value(input, :keys)
148
+ aggregate = GroupByHelpers.fetch_value(input, :aggregate)
149
+
150
+ GroupBy.new(keys: keys, aggregate: aggregate)
151
+ end
152
+
153
+ def empty?
154
+ @keys.empty? || @aggregate.nil?
155
+ end
156
+
157
+ def to_h
158
+ return {} if empty?
159
+
160
+ {
161
+ "keys" => @keys.dup,
162
+ "aggregate" => @aggregate.to_h
163
+ }
164
+ end
165
+ end
166
+ end
167
+ end
@@ -4,11 +4,12 @@ require "set"
4
4
  module Chroma
5
5
  module Search
6
6
  class Search
7
- attr_reader :where_clause, :rank_expression, :limit_config, :select_config
7
+ attr_reader :where_clause, :rank_expression, :group_by_config, :limit_config, :select_config
8
8
 
9
- def initialize(where: nil, rank: nil, limit: nil, select: nil)
9
+ def initialize(where: nil, rank: nil, group_by: nil, limit: nil, select: nil)
10
10
  @where_clause = WhereExpression.from(where) if where
11
11
  @rank_expression = RankExpression.from(rank) if rank
12
+ @group_by_config = GroupBy.from(group_by) if group_by
12
13
  @limit_config = Limit.from(limit)
13
14
  @select_config = Select.from(select)
14
15
  end
@@ -21,6 +22,10 @@ module Chroma
21
22
  clone_with(rank: RankExpression.from(rank))
22
23
  end
23
24
 
25
+ def group_by(group_by = nil)
26
+ clone_with(group_by: GroupBy.from(group_by))
27
+ end
28
+
24
29
  def limit(limit = nil, offset = nil)
25
30
  if limit.is_a?(Numeric)
26
31
  clone_with(limit: Limit.from(limit.to_i, offset))
@@ -55,15 +60,20 @@ module Chroma
55
60
 
56
61
  payload["filter"] = @where_clause.to_h if @where_clause
57
62
  payload["rank"] = @rank_expression.to_h if @rank_expression
63
+ if @group_by_config && !@group_by_config.empty?
64
+ payload["group_by"] = @group_by_config.to_h
65
+ end
58
66
  payload
59
67
  end
60
68
 
61
69
  private
62
70
 
63
- def clone_with(where: @where_clause, rank: @rank_expression, limit: @limit_config, select: @select_config)
71
+ def clone_with(where: @where_clause, rank: @rank_expression, group_by: @group_by_config,
72
+ limit: @limit_config, select: @select_config)
64
73
  instance = self.class.allocate
65
74
  instance.instance_variable_set(:@where_clause, where)
66
75
  instance.instance_variable_set(:@rank_expression, rank)
76
+ instance.instance_variable_set(:@group_by_config, group_by)
67
77
  instance.instance_variable_set(:@limit_config, limit)
68
78
  instance.instance_variable_set(:@select_config, select)
69
79
  instance
@@ -3,6 +3,7 @@
3
3
  require_relative "search/key"
4
4
  require_relative "search/where"
5
5
  require_relative "search/rank"
6
+ require_relative "search/group_by"
6
7
  require_relative "search/limit"
7
8
  require_relative "search/select"
8
9
  require_relative "search/search"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Chroma
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/chromadb.rb CHANGED
@@ -9,6 +9,7 @@ require_relative "chromadb/types"
9
9
  require_relative "chromadb/schema"
10
10
  require_relative "chromadb/search"
11
11
  require_relative "chromadb/embedding_functions"
12
+ require_relative "chromadb/client_configuration"
12
13
  require_relative "chromadb/http_client"
13
14
  require_relative "chromadb/client"
14
15
  require_relative "chromadb/admin_client"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chromadb-experimental
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chroma Core
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-12-31 00:00:00.000000000 Z
10
+ date: 2026-01-07 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: faraday
@@ -107,7 +107,7 @@ dependencies:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
109
  version: '3.23'
110
- description: Ruby client for Chroma's HTTP API (dense + sparse embeddings).
110
+ description: Experimental Ruby client for Chroma
111
111
  email:
112
112
  - support@trychroma.com
113
113
  executables: []
@@ -117,6 +117,7 @@ files:
117
117
  - lib/chromadb.rb
118
118
  - lib/chromadb/admin_client.rb
119
119
  - lib/chromadb/client.rb
120
+ - lib/chromadb/client_configuration.rb
120
121
  - lib/chromadb/collection.rb
121
122
  - lib/chromadb/embedding_functions.rb
122
123
  - lib/chromadb/embedding_functions/chroma_bm25.rb
@@ -198,6 +199,7 @@ files:
198
199
  - lib/chromadb/schemas/chroma-cloud-splade.json
199
200
  - lib/chromadb/schemas/chroma_bm25.json
200
201
  - lib/chromadb/search.rb
202
+ - lib/chromadb/search/group_by.rb
201
203
  - lib/chromadb/search/key.rb
202
204
  - lib/chromadb/search/limit.rb
203
205
  - lib/chromadb/search/rank.rb
@@ -229,5 +231,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
231
  requirements: []
230
232
  rubygems_version: 3.6.2
231
233
  specification_version: 4
232
- summary: Chroma Ruby client
234
+ summary: Chroma Ruby client (experimental)
233
235
  test_files: []