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 +4 -4
- data/README.md +53 -0
- data/docs/_layouts/home.html +1 -1
- data/docs/api/overview.md +6 -0
- data/docs/guides/middleware.md +324 -0
- data/examples/middleware_demo.rb +103 -0
- data/lib/vectra/client.rb +71 -15
- data/lib/vectra/health_check.rb +4 -2
- data/lib/vectra/middleware/base.rb +97 -0
- data/lib/vectra/middleware/cost_tracker.rb +121 -0
- data/lib/vectra/middleware/instrumentation.rb +44 -0
- data/lib/vectra/middleware/logging.rb +62 -0
- data/lib/vectra/middleware/pii_redaction.rb +65 -0
- data/lib/vectra/middleware/request.rb +62 -0
- data/lib/vectra/middleware/response.rb +65 -0
- data/lib/vectra/middleware/retry.rb +103 -0
- data/lib/vectra/middleware/stack.rb +74 -0
- data/lib/vectra/version.rb +1 -1
- data/lib/vectra.rb +9 -0
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70730299dab8475b05688f7017dccd1965b87a49589c96be7633a356af8d57f2
|
|
4
|
+
data.tar.gz: 14999ebde62586578b444ba45d396cb38f1a4dd5dcbf2fd126e042ebb1c6fae5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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:
|
data/docs/_layouts/home.html
CHANGED
|
@@ -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
|
|
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!"
|