vector_amp 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: b5cca5e94ec6fe5fa062e6e6034a5576db8264eefb4ebc306ed489ad8946ccd6
4
- data.tar.gz: ba79880edd034128d92b4a57b3a7fe46be310e0b190d3cb93f8c7a6b00d80db4
3
+ metadata.gz: 275fe25e338462efa98c327a40e334085c4cbad95e720fc3ca3adecb25afa31c
4
+ data.tar.gz: ec1e9abc640c9a76a2203550d277c20152828ff341bb91084594170cf572beae
5
5
  SHA512:
6
- metadata.gz: 8a1cf8517e4862b4a240b6dc6b4da146bb2fe1499d9b2b6a98fbdefe870be57ad24c78b5defffc4986ca3babea7d8533128450d1bfc54468305c23e54bcabcc5
7
- data.tar.gz: aca7d1ef6aa30ff9477fdcd30d1deab9831111a8fd759f521e0cbd2334f724d2b870e57eee393a9d0d987f19ced7c6b9a776054c54a44bd6f5b4a6f6f20c3aa1
6
+ metadata.gz: 3fa5d4c3be9cae14083865ee02cfbe99bd72712ecd3ee461db487f10a51ac9c24c6810616b8d918154f1016d6971bb9d5412327221bb983ab39e821940da1595
7
+ data.tar.gz: 18267f1db75a0b97685a123be17caa021594fa09f6212b6f3f99bc52554a8423e963bac34d6e0f5a266467b017d981fec0f5f6f2c6dd42ef4151ab776dc205aa
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  <div align="center">
2
2
  <a href="https://vectoramp.com/">
3
3
  <picture>
4
- <source media="(prefers-color-scheme: light)" srcset=".github/images/logo-full-light.svg">
5
- <source media="(prefers-color-scheme: dark)" srcset=".github/images/logo-full-dark.svg">
6
- <img alt="VectorAmp Logo" src=".github/images/logo-full-dark.svg" width="50%">
4
+ <source media="(prefers-color-scheme: light)" srcset="https://vectoramp.com/logo-full-light.svg">
5
+ <source media="(prefers-color-scheme: dark)" srcset="https://vectoramp.com/logo-full-dark.svg">
6
+ <img alt="VectorAmp Logo" src="https://vectoramp.com/logo-full-dark.svg" width="50%">
7
7
  </picture>
8
8
  </a>
9
9
  </div>
@@ -64,14 +64,23 @@ answer = dataset.ask("What powers VectorAmp?")
64
64
  ### Creating datasets
65
65
 
66
66
  ```ruby
67
- # Minimal: name only.
67
+ # Minimal: name only. Embedding config is omitted so VectorAmp uses
68
+ # the managed VectorAmp-Embedding-4B model and infers dim 2560.
68
69
  client.datasets.create(name: "docs")
69
70
 
70
71
  # Enable hybrid (dense + sparse) search.
71
72
  client.datasets.create(name: "docs", hybrid: true)
72
73
 
73
- # Use an OpenAI embedding model (dim inferred from the model).
74
- client.datasets.create(name: "docs", embedding: VectorAmp::Embedding.openai("large"))
74
+ # Optional BYOM: use OpenAI only when you intentionally want that provider.
75
+ # VectorAmp::Embedding.openai uses embedding.secret_ref "emb:openai:api_key" by default.
76
+ client.datasets.create(name: "openai-docs", embedding: VectorAmp::Embedding.openai("large"))
77
+
78
+ # Store/update the org OpenAI key, then create a dataset that references it.
79
+ client.datasets.create_with_openai_api_key(
80
+ name: "openai-docs",
81
+ api_key: ENV.fetch("OPENAI_API_KEY"),
82
+ size: "small"
83
+ )
75
84
 
76
85
  # Custom/unknown model: pass dim explicitly.
77
86
  client.datasets.create(name: "docs", embedding: { provider: "acme", model: "acme-embed" }, dim: 1024)
@@ -111,6 +120,7 @@ dataset.search(
111
120
  rerank: true # expands to vectoramp / VectorAmp-Rerank-v1
112
121
  )
113
122
  dataset.insert(vectors: [{ id: "sku-1", values: [0.1, 0.2], metadata: { category: "electronics" } }])
123
+ dataset.delete_vectors(ids: ["sku-1"])
114
124
  dataset.add_texts(["Wireless headphones"], metadata: { category: "electronics" })
115
125
  dataset.ask("Which headphones should I buy?")
116
126
  dataset.ingest_source("source-uuid")
@@ -123,10 +133,26 @@ dataset.search("wireless headphones", hybrid: true, sparse_query: "headphones",
123
133
  client.datasets.stats("dataset-uuid")
124
134
  client.datasets.search("dataset-uuid", "wireless headphones", top_k: 10)
125
135
  client.datasets.insert("dataset-uuid", vectors: [])
136
+ client.datasets.delete_vectors("dataset-uuid", ids: ["sku-1", 42])
126
137
  ```
127
138
 
128
139
  `client.datasets.create` intentionally does not accept `index_type`; all datasets are created with SABLE.
129
140
 
141
+ ### Organization secrets
142
+
143
+ ```ruby
144
+ # Store or rotate the default OpenAI org secret used by OpenAI embedding datasets.
145
+ client.org_secrets.put_openai_api_key(api_key: ENV.fetch("OPENAI_API_KEY"))
146
+
147
+ # Optionally validate before storing, or use a custom secret_ref.
148
+ client.org_secrets.put_openai_api_key(
149
+ api_key: ENV.fetch("OPENAI_API_KEY"),
150
+ secret_ref: "emb:openai:api_key",
151
+ validate: true,
152
+ model: "text-embedding-3-small"
153
+ )
154
+ ```
155
+
130
156
  ### Source documents
131
157
 
132
158
  Dataset document listing is cursor-based: pass `next_cursor` into the next request and do not assume offsets or totals. Downloads return retained original bytes and follow API/storage redirects.
@@ -5,6 +5,7 @@ require_relative "datasets"
5
5
  require_relative "error"
6
6
  require_relative "ingestion"
7
7
  require_relative "intelligence"
8
+ require_relative "org_secrets"
8
9
  require_relative "schedules"
9
10
  require_relative "source"
10
11
  require_relative "transport/http"
@@ -19,7 +20,8 @@ module VectorAmp
19
20
  # @return [IngestionResource] ingestion/source API resource.
20
21
  # @return [IntelligenceResource] intelligence API resource.
21
22
  # @return [ConnectionsResource] managed provider connections API resource.
22
- attr_reader :base_url, :datasets, :ingestion, :intelligence, :schedules, :sources, :connections
23
+ # @return [OrgSecretsResource] organization-scoped secret helpers.
24
+ attr_reader :base_url, :datasets, :ingestion, :intelligence, :schedules, :sources, :connections, :org_secrets
23
25
 
24
26
  # Create a VectorAmp API client.
25
27
  # @param api_key [String] API key; defaults to ENV["VECTORAMP_API_KEY"].
@@ -38,6 +40,7 @@ module VectorAmp
38
40
  @schedules = SchedulesResource.new(@transport)
39
41
  @intelligence = IntelligenceResource.new(@transport)
40
42
  @connections = ConnectionsResource.new(@transport)
43
+ @org_secrets = OrgSecretsResource.new(@transport)
41
44
  @datasets = DatasetsResource.new(@transport, client: self)
42
45
  end
43
46
 
@@ -71,6 +71,14 @@ module VectorAmp
71
71
  service.insert(id, vectors: vectors)
72
72
  end
73
73
 
74
+ # Delete one or more vectors from this dataset by id.
75
+ # @param ids [Array<String,Integer>] vector ids to delete.
76
+ # @param write_concern [String, nil] optional API write concern.
77
+ # @return [Hash] delete response.
78
+ def delete_vectors(ids:, write_concern: nil)
79
+ service.delete_vectors(id, ids: ids, write_concern: write_concern)
80
+ end
81
+
74
82
  # Embed and insert texts into this dataset.
75
83
  # @param texts_arg [Array<String>, nil] positional texts for convenience.
76
84
  # @param texts [Array<String>, nil] keyword texts.
@@ -92,6 +92,29 @@ module VectorAmp
92
92
  wrap_dataset(@transport.request(:post, "/datasets", body: body))
93
93
  end
94
94
 
95
+ # Store/update an OpenAI API key in org secrets, then create an OpenAI-backed dataset
96
+ # whose embedding config references that secret.
97
+ # @param name [String] dataset name.
98
+ # @param api_key [String] OpenAI API key to store server-side.
99
+ # @param size [String, Symbol] "small" or "large".
100
+ # @param secret_ref [String] org secret reference to write and attach to the dataset.
101
+ # @param validate [Boolean] validate the key before storing.
102
+ # @param options [Hash] forwarded to {#create}, e.g. `hybrid: true` or `metadata:`.
103
+ # @return [Dataset] created dataset.
104
+ def create_with_openai_api_key(name:, api_key:, size: "small", secret_ref: "emb:openai:api_key", validate: false, **options)
105
+ secrets = @client&.org_secrets || begin
106
+ require_relative "org_secrets"
107
+ OrgSecretsResource.new(@transport)
108
+ end
109
+ secrets.put_openai_api_key(
110
+ api_key: api_key,
111
+ secret_ref: secret_ref,
112
+ validate: validate,
113
+ model: openai_model(size)
114
+ )
115
+ create(name: name, embedding: Embedding.openai(size, secret_ref: secret_ref), **options)
116
+ end
117
+
95
118
 
96
119
  # List retained source documents for a dataset using cursor pagination.
97
120
  # @param dataset_id [String] dataset id.
@@ -176,6 +199,21 @@ module VectorAmp
176
199
  @transport.request(:post, "/datasets/#{dataset_id}/insert", body: { vectors: Utils.normalize_vectors(vectors) })
177
200
  end
178
201
 
202
+ # Delete one or more vectors from a dataset by id.
203
+ # @param dataset_id [String] dataset id.
204
+ # @param ids [Array<String,Integer>] vector ids to delete.
205
+ # @param write_concern [String, nil] optional API write concern.
206
+ # @return [Hash] delete response with `deleted` and `dataset_id`.
207
+ def delete_vectors(dataset_id, ids:, write_concern: nil)
208
+ raise ArgumentError, "ids must not be empty" if ids.nil? || ids.empty?
209
+
210
+ @transport.request(
211
+ :delete,
212
+ "/datasets/#{dataset_id}/vectors",
213
+ body: Utils.compact_hash(ids: ids, write_concern: write_concern)
214
+ )
215
+ end
216
+
179
217
  # Generate embeddings using the dataset embedding configuration.
180
218
  # @param dataset_id [String] dataset id.
181
219
  # @param text [String, nil] single text to embed.
@@ -221,6 +259,15 @@ module VectorAmp
221
259
 
222
260
  private
223
261
 
262
+ def openai_model(size)
263
+ case size.to_s
264
+ when "small" then "text-embedding-3-small"
265
+ when "large" then "text-embedding-3-large"
266
+ else
267
+ raise ArgumentError, %(openai size must be "small" or "large", got #{size.inspect})
268
+ end
269
+ end
270
+
224
271
  def wrap_dataset(data)
225
272
  Dataset.new(data, service: self, client: @client)
226
273
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "utils"
4
+
3
5
  module VectorAmp
4
6
  # Embedding configuration defaults and helpers.
5
7
  #
@@ -28,16 +30,17 @@ module VectorAmp
28
30
 
29
31
  # Build an OpenAI embedding configuration.
30
32
  # @param size [String, Symbol] `"small"` (text-embedding-3-small) or `"large"` (text-embedding-3-large).
31
- # @return [Hash] `{ provider: "openai", model: ... }`
33
+ # @param secret_ref [String, nil] organization secret reference containing the OpenAI API key.
34
+ # @return [Hash] `{ provider: "openai", model: ..., secret_ref: ... }`
32
35
  # @raise [ArgumentError] when size is not "small" or "large".
33
- def openai(size = "small")
36
+ def openai(size = "small", secret_ref: "emb:openai:api_key")
34
37
  model = case size.to_s
35
38
  when "small" then "text-embedding-3-small"
36
39
  when "large" then "text-embedding-3-large"
37
40
  else
38
41
  raise ArgumentError, %(openai size must be "small" or "large", got #{size.inspect})
39
42
  end
40
- { provider: "openai", model: model }
43
+ Utils.compact_hash(provider: "openai", model: model, secret_ref: secret_ref)
41
44
  end
42
45
 
43
46
  # Normalize a user-supplied embedding value into a config hash.
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ require_relative "utils"
6
+
7
+ module VectorAmp
8
+ # Organization-scoped secret helpers exposed by the public API.
9
+ class OrgSecretsResource
10
+ DEFAULT_OPENAI_SECRET_REF = "emb:openai:api_key"
11
+
12
+ # @param transport [#request] API transport.
13
+ # @return [OrgSecretsResource]
14
+ def initialize(transport)
15
+ @transport = transport
16
+ end
17
+
18
+ # Store or update an organization secret by name.
19
+ # @param name [String] org secret reference to write.
20
+ # @param value [String] plaintext value sent once to the API.
21
+ def put(name:, value:)
22
+ secret_name = name.to_s.strip
23
+ secret_value = value.to_s.strip
24
+ raise ArgumentError, "name is required" if secret_name.empty?
25
+ raise ArgumentError, "value is required" if secret_value.empty?
26
+
27
+ @transport.request(:put, "/org-secrets/#{URI.encode_www_form_component(secret_name)}", body: { value: secret_value })
28
+ end
29
+
30
+ # Check whether an organization secret exists.
31
+ def exists?(name:)
32
+ secret_name = name.to_s.strip
33
+ raise ArgumentError, "name is required" if secret_name.empty?
34
+ @transport.request(:get, "/org-secrets/#{URI.encode_www_form_component(secret_name)}")
35
+ end
36
+
37
+ # Store or update the organization OpenAI API key.
38
+ # @param api_key [String] OpenAI API key to store server-side.
39
+ # @param secret_ref [String] org secret reference to write.
40
+ # @param validate [Boolean] accepted for API compatibility; generic org-secret writes do not validate.
41
+ # @param model [String, nil] accepted for API compatibility.
42
+ # @return [Hash, nil] API response; normally nil/empty for 204.
43
+ def put_openai_api_key(api_key:, secret_ref: DEFAULT_OPENAI_SECRET_REF, validate: false, model: nil)
44
+ key = api_key.to_s.strip
45
+ raise ArgumentError, "api_key is required" if key.empty?
46
+
47
+ put(name: secret_ref, value: key)
48
+ end
49
+
50
+ alias update_openai_api_key put_openai_api_key
51
+
52
+ # Check whether the default organization OpenAI API key exists.
53
+ # @return [Hash, nil] API response; normally nil/empty for 204.
54
+ def openai_api_key?
55
+ exists?(name: DEFAULT_OPENAI_SECRET_REF)
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module VectorAmp
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/vector_amp.rb CHANGED
@@ -4,6 +4,7 @@ require_relative "vector_amp/version"
4
4
  require_relative "vector_amp/client"
5
5
  require_relative "vector_amp/embedding"
6
6
  require_relative "vector_amp/error"
7
+ require_relative "vector_amp/org_secrets"
7
8
  require_relative "vector_amp/source"
8
9
 
9
10
  module VectorAmp
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vector_amp
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
  - VectorAmp
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-02 00:00:00.000000000 Z
11
+ date: 2026-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -114,6 +114,7 @@ files:
114
114
  - lib/vector_amp/error.rb
115
115
  - lib/vector_amp/ingestion.rb
116
116
  - lib/vector_amp/intelligence.rb
117
+ - lib/vector_amp/org_secrets.rb
117
118
  - lib/vector_amp/schedules.rb
118
119
  - lib/vector_amp/source.rb
119
120
  - lib/vector_amp/transport/base.rb