vectra-client 1.0.8 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '05778206f66d4e2ead830f5b7c5885f6336b46d36af696f3df5a56eb26163c5f'
4
- data.tar.gz: 1ae5814cd10df006ecf5568f34e457400b072ecc8a06dbf85a4e4f5b94120fb9
3
+ metadata.gz: 70730299dab8475b05688f7017dccd1965b87a49589c96be7633a356af8d57f2
4
+ data.tar.gz: 14999ebde62586578b444ba45d396cb38f1a4dd5dcbf2fd126e042ebb1c6fae5
5
5
  SHA512:
6
- metadata.gz: d25889a4626c9caa9edf7ccc285c849799de87cf38a8a7a6f56a49edb46da592b28bc0232960e2612fd0d9199e990c21bd5fe6b88f15351d8ff38c73bd7afc84
7
- data.tar.gz: d4ef355089814ed4caf8c61e216b9554f23035df12be72a65c778b3230bd2a32f16ecf4c50de3bc33e40dc323f1aee83d7a891ad962bec105585c8bc6b30902b
6
+ metadata.gz: 69c8fa722ee4abfe3ddf6b19f8bd12f46d09edefee50612021142c3951600666ed854018e8a5c1bc33249895fec42ae18ab1d821beffa989d2700da99c28bcf4
7
+ data.tar.gz: f27a0df4bbcf618659297b1376ebe977588d32d5dfef76fe7c8f5f38c8780b37e055a0e8d91f6bc3ae8eb7cad88606c760d4ed5882c61756ac1495dedfc339a5
data/README.md CHANGED
@@ -242,6 +242,59 @@ Real-world patterns for common use cases:
242
242
 
243
243
  👉 **[Browse all Recipes & Patterns](https://vectra-docs.netlify.app/guides/recipes/)**
244
244
 
245
+ ## Middleware System
246
+
247
+ Vectra features a powerful **Rack-style middleware system** that allows you to extend functionality without modifying core code.
248
+
249
+ ### Quick Start
250
+
251
+ ```ruby
252
+ # Global middleware (applies to all clients)
253
+ Vectra::Client.use Vectra::Middleware::Logging
254
+ Vectra::Client.use Vectra::Middleware::Retry, max_attempts: 5
255
+ Vectra::Client.use Vectra::Middleware::CostTracker
256
+
257
+ # Per-client middleware
258
+ client = Vectra::Client.new(
259
+ provider: :qdrant,
260
+ middleware: [
261
+ Vectra::Middleware::PIIRedaction,
262
+ Vectra::Middleware::Instrumentation
263
+ ]
264
+ )
265
+ ```
266
+
267
+ ### Built-in Middleware
268
+
269
+ - **Logging** - Request/response logging with timing
270
+ - **Retry** - Automatic retries with exponential backoff
271
+ - **Instrumentation** - Metrics and monitoring integration
272
+ - **PIIRedaction** - Automatic PII (email, phone, SSN) redaction
273
+ - **CostTracker** - Track API usage costs per operation
274
+
275
+ ### Custom Middleware
276
+
277
+ ```ruby
278
+ class MyAuditMiddleware < Vectra::Middleware::Base
279
+ def before(request)
280
+ # Called before the operation
281
+ AuditLog.create!(action: request.operation, user: Current.user)
282
+ end
283
+
284
+ def after(request, response)
285
+ # Called after successful operation
286
+ puts "Duration: #{response.metadata[:duration_ms]}ms"
287
+ end
288
+
289
+ def on_error(request, error)
290
+ # Called when an error occurs
291
+ ErrorTracker.notify(error, context: { operation: request.operation })
292
+ end
293
+ end
294
+
295
+ Vectra::Client.use MyAuditMiddleware
296
+ ```
297
+
245
298
  ## Production Patterns
246
299
 
247
300
  Vectra includes 7 production-ready patterns out of the box:
@@ -55,7 +55,7 @@
55
55
  <!-- Hero Section -->
56
56
  <section class="tma-hero">
57
57
  <div class="tma-hero__container">
58
- <span class="tma-hero__badge">v1.0.1 — Hybrid Search & Rails Generator</span>
58
+ <span class="tma-hero__badge">v1.1.0 — Hybrid Search, Rails Generator & Middleware</span>
59
59
  <h1 class="tma-hero__title">
60
60
  Vector Databases,<br>
61
61
  <span class="tma-hero__title-gradient">Unified for Ruby.</span>
data/docs/api/overview.md CHANGED
@@ -216,3 +216,9 @@ end
216
216
  ```
217
217
 
218
218
  See [Complete API Methods Reference]({{ site.baseurl }}/api/methods/) for detailed method documentation.
219
+
220
+ ## Next Steps
221
+
222
+ - [Providers Guide]({{ site.baseurl }}/providers/)
223
+ - [Rails Integration Guide]({{ site.baseurl }}/guides/rails-integration/)
224
+ - [Middleware System]({{ site.baseurl }}/guides/middleware/)
@@ -0,0 +1,324 @@
1
+ ---
2
+ layout: page
3
+ title: Middleware System
4
+ permalink: /guides/middleware/
5
+ ---
6
+
7
+ # Middleware System
8
+
9
+ Vectra includes a **Rack-style middleware stack** that lets you extend the client
10
+ without forking the gem or patching providers.
11
+
12
+ You can:
13
+
14
+ - Add **logging, metrics, retries, PII redaction, cost tracking**.
15
+ - Inject **custom behaviour** before/after every operation.
16
+ - Enable features **globally** or **per client**, same kao Faraday/Sidekiq.
17
+
18
+ ---
19
+
20
+ ## Core Concepts
21
+
22
+ ### Request / Response
23
+
24
+ - **`Vectra::Middleware::Request`**
25
+ - `operation` – npr. `:upsert`, `:query`, `:fetch`, `:delete`, `:stats`, `:hybrid_search`
26
+ - `index` – ime indeksa (može biti `nil` ako koristiš default)
27
+ - `namespace` – namespace (može biti `nil`)
28
+ - `params` – originalni keyword parametri koje je `Vectra::Client` proslijedio
29
+ - `provider` – ime providera (`:pinecone`, `:qdrant`, `:pgvector`, `:memory`, …)
30
+ - helperi: `write_operation?`, `read_operation?`
31
+
32
+ - **`Vectra::Middleware::Response`**
33
+ - `result` – što god provider vrati (npr. hash, `QueryResult`, itd.)
34
+ - `error` – iznimka ako je došlo do greške
35
+ - `metadata` – slobodan hash za dodatne informacije (trajanje, cost, retry_count…)
36
+ - helperi: `success?`, `failure?`, `raise_if_error!`, `value!`
37
+
38
+ ### Base i Stack
39
+
40
+ - **`Vectra::Middleware::Base`**
41
+ - Hookovi koje možeš override-ati:
42
+ - `before(request)` – prije poziva providera / sljedećeg middlewarea
43
+ - `after(request, response)` – nakon uspješnog poziva
44
+ - `on_error(request, error)` – kad dođe do iznimke (error se zatim re-raise-a)
45
+
46
+ - **`Vectra::Middleware::Stack`**
47
+ - Gradi chain oko konkretnog providera:
48
+ ```ruby
49
+ stack = Vectra::Middleware::Stack.new(provider, [Logging.new, Retry.new])
50
+ result = stack.call(:upsert, index: "docs", vectors: [...], provider: :qdrant)
51
+ ```
52
+ - `Stack` interno:
53
+ - kreira `Request`,
54
+ - kroz sve middlewares propagira isti `Request`,
55
+ - na kraju zove `provider.public_send(request.operation, **provider_params)`,
56
+ - vraća `Response` (s `result` ili `error`).
57
+
58
+ ---
59
+
60
+ ## Enabling Middleware
61
+
62
+ ### Global Middleware
63
+
64
+ Primjenjuje se na **sve** `Vectra::Client` instance.
65
+
66
+ ```ruby
67
+ require "vectra"
68
+
69
+ # Global logging + retry + cost tracking
70
+ Vectra::Client.use Vectra::Middleware::Logging
71
+ Vectra::Client.use Vectra::Middleware::Retry, max_attempts: 5
72
+ Vectra::Client.use Vectra::Middleware::CostTracker
73
+ ```
74
+
75
+ Sve sljedeće `Vectra::Client.new(...)` instance će koristiti ovaj globalni stack.
76
+
77
+ ### Per‑Client Middleware
78
+
79
+ Dodatni ili prilagođeni middleware samo za jedan klijent:
80
+
81
+ ```ruby
82
+ client = Vectra::Client.new(
83
+ provider: :qdrant,
84
+ index: "products",
85
+ middleware: [
86
+ Vectra::Middleware::PIIRedaction,
87
+ Vectra::Middleware::Instrumentation
88
+ ]
89
+ )
90
+ ```
91
+
92
+ - Per‑client middleware se izvodi **nakon** globalnog, u istom chainu.
93
+ - Redoslijed u arrayu definira redoslijed ekzekucije (zadnji je najunutarnji, tik do providera).
94
+
95
+ ---
96
+
97
+ ## Koje operacije prolaze kroz stack?
98
+
99
+ Sve standardne operacije `Vectra::Client`a koriste middleware stack:
100
+
101
+ - `upsert`
102
+ - `query`
103
+ - `fetch`
104
+ - `update`
105
+ - `delete`
106
+ - `list_indexes`
107
+ - `describe_index`
108
+ - `stats`
109
+ - `hybrid_search`
110
+
111
+ To znači da middleware može:
112
+
113
+ - logirati / instrumentirati **sve pozive** prema provideru,
114
+ - raditi **PII redakciju** na `upsert` zahtjevima,
115
+ - brojati i retry-ati i **read** i **write** operacije,
116
+ - računati trošak po operaciji (npr. za billing / budžete).
117
+
118
+ ---
119
+
120
+ ## Built‑in Middleware
121
+
122
+ ### Logging (`Vectra::Middleware::Logging`)
123
+
124
+ **Što radi:**
125
+ - logira početak i kraj svake operacije (`operation`, `index`, `namespace`),
126
+ - mjeri trajanje i sprema ga u `response.metadata[:duration_ms]`.
127
+
128
+ **Konfiguracija:**
129
+
130
+ ```ruby
131
+ # Globalno
132
+ Vectra::Client.use Vectra::Middleware::Logging
133
+
134
+ # S custom loggerom
135
+ logger = Logger.new($stdout)
136
+ Vectra::Client.use Vectra::Middleware::Logging, logger: logger
137
+ ```
138
+
139
+ **Tipična upotreba:** debugiranje, audit logovi, korelacija s HTTP logovima.
140
+
141
+ ---
142
+
143
+ ### Retry (`Vectra::Middleware::Retry`)
144
+
145
+ **Što radi:**
146
+ - automatski retry-a transient greške:
147
+ - `Vectra::RateLimitError`
148
+ - `Vectra::ConnectionError`
149
+ - `Vectra::TimeoutError`
150
+ - `Vectra::ServerError`
151
+ - koristi exponential ili linear backoff,
152
+ - upisuje broj retry-a u `response.metadata[:retry_count]`.
153
+
154
+ **Konfiguracija:**
155
+
156
+ ```ruby
157
+ # 3 pokušaja, exponential backoff (default)
158
+ Vectra::Client.use Vectra::Middleware::Retry
159
+
160
+ # 5 pokušaja, linearni backoff
161
+ Vectra::Client.use Vectra::Middleware::Retry,
162
+ max_attempts: 5,
163
+ backoff: :linear
164
+
165
+ # Fiksni delay 1.0s
166
+ Vectra::Client.use Vectra::Middleware::Retry,
167
+ max_attempts: 3,
168
+ backoff: 1.0
169
+ ```
170
+
171
+ **Tipična upotreba:** zaštita od povremenih mrežnih problema i rate‑limit grešaka.
172
+
173
+ ---
174
+
175
+ ### Instrumentation (`Vectra::Middleware::Instrumentation`)
176
+
177
+ **Što radi:**
178
+ - emitira događaje preko postojećeg `Vectra::Instrumentation` sustava,
179
+ - prikuplja trajanje, status, error class, dodatni `metadata`.
180
+
181
+ **Primjer:**
182
+
183
+ ```ruby
184
+ Vectra::Client.use Vectra::Middleware::Instrumentation
185
+
186
+ Vectra.on_operation do |event|
187
+ # event[:operation], event[:provider], event[:duration_ms], event[:success], ...
188
+ StatsD.timing("vectra.#{event[:operation]}", event[:duration_ms])
189
+ end
190
+ ```
191
+
192
+ **Tipična upotreba:** integracija s Prometheus/Grafana, Datadog, New Relic…
193
+
194
+ ---
195
+
196
+ ### PII Redaction (`Vectra::Middleware::PIIRedaction`)
197
+
198
+ **Što radi:**
199
+ - prije `upsert` operacija prolazi kroz `vectors[:metadata]`,
200
+ - prepoznaje PII pattern-e (email, phone, SSN, credit card) i zamjenjuje ih placeholderom
201
+ npr. `[REDACTED_EMAIL]`, `[REDACTED_PHONE]`, itd.
202
+
203
+ **Primjer:**
204
+
205
+ ```ruby
206
+ Vectra::Client.use Vectra::Middleware::PIIRedaction
207
+
208
+ client.upsert(
209
+ index: "sensitive",
210
+ vectors: [
211
+ {
212
+ id: "user-1",
213
+ values: [0.1, 0.2, 0.3],
214
+ metadata: {
215
+ email: "user@example.com",
216
+ phone: "555-1234",
217
+ note: "Contact at user@example.com"
218
+ }
219
+ }
220
+ ]
221
+ )
222
+ ```
223
+
224
+ Nakon `upsert`‑a, provider će vidjeti već **redaktirani** metadata.
225
+
226
+ **Custom patterni:**
227
+
228
+ ```ruby
229
+ patterns = {
230
+ credit_card: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/,
231
+ api_key: /sk-[a-zA-Z0-9]{32}/
232
+ }
233
+
234
+ Vectra::Client.use Vectra::Middleware::PIIRedaction, patterns: patterns
235
+ ```
236
+
237
+ **Tipična upotreba:** GDPR, SOC2, PCI‑DSS okruženja gdje je zabranjen PII u vektor bazi.
238
+
239
+ ---
240
+
241
+ ### CostTracker (`Vectra::Middleware::CostTracker`)
242
+
243
+ **Što radi:**
244
+ - procjenjuje trošak po operaciji na temelju providera i tipa operacije (`read` / `write`),
245
+ - upisuje trošak u `response.metadata[:cost_usd]`,
246
+ - opcionalno zove `on_cost` callback za real‑time praćenje.
247
+
248
+ **Primjer:**
249
+
250
+ ```ruby
251
+ Vectra::Client.use Vectra::Middleware::CostTracker,
252
+ on_cost: ->(event) {
253
+ puts "💰 Cost: $#{event[:cost_usd].round(6)} for #{event[:operation]} (#{event[:provider]})"
254
+ }
255
+ ```
256
+
257
+ **Custom pricing:**
258
+
259
+ ```ruby
260
+ pricing = {
261
+ pinecone: { read: 0.0001, write: 0.0002 },
262
+ qdrant: { read: 0.00005, write: 0.0001 }
263
+ }
264
+
265
+ Vectra::Client.use Vectra::Middleware::CostTracker, pricing: pricing
266
+ ```
267
+
268
+ **Tipična upotreba:** unutarnji billing, budget guardrails, cost dashboards.
269
+
270
+ ---
271
+
272
+ ## Custom Middleware
273
+
274
+ Najjednostavniji način je naslijediti `Vectra::Middleware::Base` i override-ati hookove:
275
+
276
+ ```ruby
277
+ class MyAuditMiddleware < Vectra::Middleware::Base
278
+ def before(request)
279
+ AuditLog.create!(
280
+ operation: request.operation,
281
+ index: request.index,
282
+ namespace: request.namespace,
283
+ provider: request.provider
284
+ )
285
+ end
286
+
287
+ def after(_request, response)
288
+ puts "Duration: #{response.metadata[:duration_ms]}ms"
289
+ end
290
+
291
+ def on_error(request, error)
292
+ ErrorTracker.notify(error, context: { operation: request.operation })
293
+ end
294
+ end
295
+
296
+ Vectra::Client.use MyAuditMiddleware
297
+ ```
298
+
299
+ **Savjeti:**
300
+
301
+ - Ne mijenjaj strukturu `request.params` na način koji provider ne očekuje.
302
+ - Svoje pomoćne podatke stavljaj u `response.metadata` ili `request.metadata`.
303
+ - Ako hvataš greške u `on_error`, **nemoj ih gutati** – middleware stack će ih ponovo baciti.
304
+
305
+ ---
306
+
307
+ ## Primjer: `examples/middleware_demo.rb`
308
+
309
+ U repozitoriju imaš kompletan demo:
310
+
311
+ - konfigurira globalni stack (`Logging`, `Retry`, `CostTracker`),
312
+ - pokazuje per‑client PII redaction,
313
+ - definira custom `TimingMiddleware`,
314
+ - demonstrira kako izgleda output u konzoli.
315
+
316
+ Pokretanje:
317
+
318
+ ```bash
319
+ bundle exec ruby examples/middleware_demo.rb
320
+ ```
321
+
322
+ Ovaj demo je dobar “živi” primjer kako kombinirati više middleware-a u praksi.
323
+
324
+
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Middleware System Demo
5
+ #
6
+ # This script demonstrates the new middleware system in Vectra.
7
+ # Run with: ruby examples/middleware_demo.rb
8
+
9
+ require_relative "../lib/vectra"
10
+
11
+ # Configure global middleware
12
+ puts "🎯 Configuring global middleware..."
13
+ Vectra::Client.use Vectra::Middleware::Logging
14
+ Vectra::Client.use Vectra::Middleware::Retry, max_attempts: 3
15
+ Vectra::Client.use Vectra::Middleware::CostTracker, on_cost: ->(event) {
16
+ puts "💰 Cost: $#{event[:cost_usd].round(6)} for #{event[:operation]}"
17
+ }
18
+
19
+ # Create client
20
+ puts "\n📦 Creating client with Memory provider..."
21
+ client = Vectra::Client.new(
22
+ provider: :memory,
23
+ index: "demo"
24
+ )
25
+
26
+ # Example 1: Upsert with middleware
27
+ puts "\n🔄 Example 1: Upsert with middleware stack"
28
+ puts "=" * 50
29
+ client.upsert(
30
+ index: "demo",
31
+ vectors: [
32
+ { id: "doc-1", values: [0.1, 0.2, 0.3], metadata: { title: "Ruby" } },
33
+ { id: "doc-2", values: [0.4, 0.5, 0.6], metadata: { title: "Python" } }
34
+ ]
35
+ )
36
+
37
+ # Example 2: Query with middleware
38
+ puts "\n🔍 Example 2: Query with middleware stack"
39
+ puts "=" * 50
40
+ results = client.query(
41
+ index: "demo",
42
+ vector: [0.1, 0.2, 0.3],
43
+ top_k: 2
44
+ )
45
+ puts "Found #{results.size} results"
46
+
47
+ # Example 3: Per-client middleware
48
+ puts "\n🎨 Example 3: Per-client middleware (PII Redaction)"
49
+ puts "=" * 50
50
+ pii_client = Vectra::Client.new(
51
+ provider: :memory,
52
+ index: "sensitive",
53
+ middleware: [Vectra::Middleware::PIIRedaction]
54
+ )
55
+
56
+ pii_client.upsert(
57
+ index: "sensitive",
58
+ vectors: [
59
+ {
60
+ id: "user-1",
61
+ values: [0.1, 0.2, 0.3],
62
+ metadata: {
63
+ email: "user@example.com",
64
+ phone: "555-1234",
65
+ note: "Contact at user@example.com"
66
+ }
67
+ }
68
+ ]
69
+ )
70
+
71
+ # Fetch to see redacted data
72
+ fetched = pii_client.fetch(index: "sensitive", ids: ["user-1"])
73
+ puts "Original email: user@example.com"
74
+ puts "Redacted: #{fetched["user-1"].metadata[:email]}"
75
+ puts "Redacted note: #{fetched["user-1"].metadata[:note]}"
76
+
77
+ # Example 4: Custom middleware
78
+ puts "\n🛠️ Example 4: Custom middleware"
79
+ puts "=" * 50
80
+
81
+ class TimingMiddleware < Vectra::Middleware::Base
82
+ def before(request)
83
+ puts "⏱️ Starting #{request.operation}..."
84
+ end
85
+
86
+ def after(request, response)
87
+ duration = response.metadata[:duration_ms] || 0
88
+ puts "✅ Completed in #{duration.round(2)}ms"
89
+ end
90
+ end
91
+
92
+ custom_client = Vectra::Client.new(
93
+ provider: :memory,
94
+ index: "custom",
95
+ middleware: [TimingMiddleware]
96
+ )
97
+
98
+ custom_client.upsert(
99
+ index: "custom",
100
+ vectors: [{ id: "test", values: [1, 2, 3] }]
101
+ )
102
+
103
+ puts "\n✨ Demo complete!"