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 +4 -4
- data/lib/chromadb/client.rb +9 -6
- data/lib/chromadb/client_configuration.rb +46 -0
- data/lib/chromadb/collection.rb +2 -1
- data/lib/chromadb/openapi/lib/chromadb/version.rb +1 -1
- data/lib/chromadb/search/group_by.rb +167 -0
- data/lib/chromadb/search/search.rb +13 -3
- data/lib/chromadb/search.rb +1 -0
- data/lib/chromadb/version.rb +1 -1
- data/lib/chromadb.rb +1 -0
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2638e8c25b5a39193b9d3ec22a8196b9f093dd09a0bd39df754fd69d26969fa5
|
|
4
|
+
data.tar.gz: 14147058836b4dc4f1e5bf0fc1d763c756686bfe2fe604ce4341966ac78658fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bddbe461695418288ae99b9f11d2d2d8fd64e054f8c897b28b89c1bdbfea3a4a121a62dff7412378e51e1cd4995d0a3680daa3a010eefaedb47caf8205845feb
|
|
7
|
+
data.tar.gz: 825d6d1999f39010968528ede4fa5144c32be4d7fb646a30b0b98c28ba7b2a0dee54d05c8cfd18dc797bf1465be5357ecee22b961cf1a594e03c6e65e98e06eb
|
data/lib/chromadb/client.rb
CHANGED
|
@@ -293,18 +293,21 @@ module Chroma
|
|
|
293
293
|
end
|
|
294
294
|
|
|
295
295
|
class CloudClient < Client
|
|
296
|
-
def initialize(
|
|
297
|
-
|
|
298
|
-
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:
|
|
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
|
data/lib/chromadb/collection.rb
CHANGED
|
@@ -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: { "
|
|
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
|
|
@@ -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,
|
|
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
|
data/lib/chromadb/search.rb
CHANGED
data/lib/chromadb/version.rb
CHANGED
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.
|
|
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:
|
|
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
|
|
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: []
|