vectra-client 1.1.1 → 1.1.2
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/CHANGELOG.md +20 -33
- data/README.md +2 -0
- data/docs/_layouts/home.html +1 -1
- data/docs/_layouts/page.html +22 -0
- data/docs/api/cheatsheet.md +10 -0
- data/docs/api/methods.md +24 -0
- data/docs/api/overview.md +36 -5
- data/docs/assets/style.css +131 -0
- data/docs/guides/rails-integration.md +2 -0
- data/docs/search.json +26 -0
- data/lib/vectra/client.rb +58 -1
- data/lib/vectra/health_check.rb +3 -3
- data/lib/vectra/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f59705f200c8a164cc9303e6761776af49e4a81a88f101eae2e371eccca8807
|
|
4
|
+
data.tar.gz: 217dd74d00151f3ba6e94ed80718999656f9616b84e843dd937197cee612540a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e62f19e82dfb88a14ae50f7cee6afdd8d058ece9972de8f447ba3cc881420d401cfd0d3a093d299e8273795958085349a8d3e6416ecd429f50ea2a05103dea3
|
|
7
|
+
data.tar.gz: ace8ecc519dc588f917e6b29721ed9cfa3ff9b3f3fd7b3e82911233ed7d6e59a9f443e81ae9c0d20dd4891f904d8497cb506064964b00b3dc7587f12137e12bf
|
data/CHANGELOG.md
CHANGED
|
@@ -1,42 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v1.1.1](https://github.com/stokry/vectra/tree/v1.1.1) (2026-01-15)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/stokry/vectra/compare/v1.1.0...v1.1.1)
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Text Search Support** - New `text_search` method for keyword-only search without requiring embeddings
|
|
9
|
+
- Qdrant: BM25 text search
|
|
10
|
+
- Weaviate: BM25 text search via GraphQL
|
|
11
|
+
- pgvector: PostgreSQL full-text search (`to_tsvector`, `plainto_tsquery`, `ts_rank`)
|
|
12
|
+
- Memory: Simple keyword matching (for testing)
|
|
13
|
+
- Raises `UnsupportedFeatureError` for Pinecone (use sparse vectors instead)
|
|
14
|
+
- **Documentation Search** - Added search functionality to documentation site with `simple-jekyll-search`
|
|
15
|
+
- Client-side search with fuzzy matching
|
|
16
|
+
- Search index auto-generated from all documentation pages
|
|
17
|
+
- Responsive search UI in navigation
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Updated API documentation to include `text_search` method in overview and cheatsheet
|
|
21
|
+
- Enhanced documentation with text search examples and use cases
|
|
22
|
+
|
|
3
23
|
## [v1.1.0](https://github.com/stokry/vectra/tree/v1.1.0) (2026-01-15)
|
|
4
24
|
|
|
5
25
|
[Full Changelog](https://github.com/stokry/vectra/compare/v1.0.8...v1.1.0)
|
|
6
26
|
|
|
7
|
-
### 🎉 Major Feature: Middleware System
|
|
8
|
-
|
|
9
|
-
This release introduces a **Rack-style middleware system** for all vector database operations.
|
|
10
|
-
|
|
11
|
-
#### Added
|
|
12
|
-
|
|
13
|
-
- **Middleware Stack** - All client operations now route through a composable middleware pipeline
|
|
14
|
-
- **5 Built-in Middleware**:
|
|
15
|
-
- `Vectra::Middleware::Logging` - Structured logs with timing for all operations
|
|
16
|
-
- `Vectra::Middleware::Retry` - Automatic retry with exponential/linear backoff for transient errors
|
|
17
|
-
- `Vectra::Middleware::Instrumentation` - Hooks for metrics and APM integration
|
|
18
|
-
- `Vectra::Middleware::PIIRedaction` - Automatic PII redaction (email, phone, SSN, credit cards)
|
|
19
|
-
- `Vectra::Middleware::CostTracker` - Track API costs per operation with callbacks
|
|
20
|
-
- **Request/Response Objects** - Type-safe objects with metadata attachment
|
|
21
|
-
- **Extensible Framework** - Create custom middleware by extending `Vectra::Middleware::Base`
|
|
22
|
-
- **Global & Per-Client Middleware** - Apply middleware globally (`Client.use`) or per-instance (`new(middleware: [...])`)
|
|
23
|
-
|
|
24
|
-
#### Changed
|
|
25
|
-
|
|
26
|
-
- All client operations (`upsert`, `query`, `fetch`, `update`, `delete`, `stats`, `list_indexes`, etc.) now route through middleware stack for consistency
|
|
27
|
-
- Middleware has complete visibility into all client operations
|
|
28
|
-
|
|
29
|
-
#### Documentation
|
|
30
|
-
|
|
31
|
-
- Added comprehensive middleware section to README
|
|
32
|
-
- Created `examples/middleware_demo.rb` demonstrating all 5 built-in middleware
|
|
33
|
-
- Full YARD documentation for all middleware classes
|
|
34
|
-
- Published [middleware guide](https://dev.to/stokry/rack-style-middleware-for-vector-databases-in-ruby-vectra-client-110-2jh3) on Dev.to
|
|
35
|
-
|
|
36
|
-
#### Migration Notes
|
|
37
|
-
|
|
38
|
-
No breaking changes. Middleware is opt-in - existing code works without modification.
|
|
39
|
-
|
|
40
27
|
## [v1.0.8](https://github.com/stokry/vectra/tree/v1.0.8) (2026-01-14)
|
|
41
28
|
|
|
42
29
|
[Full Changelog](https://github.com/stokry/vectra/compare/v1.0.7...v1.0.8)
|
data/README.md
CHANGED
|
@@ -226,6 +226,8 @@ This will:
|
|
|
226
226
|
- **Update the model** to include `ProductVector`
|
|
227
227
|
- **Append to `config/vectra.yml`** with index metadata (no API keys)
|
|
228
228
|
|
|
229
|
+
When `config/vectra.yml` contains exactly one entry, a plain `Vectra::Client.new` in that Rails app will automatically use that entry's `index` (and `namespace` if present) as its defaults, so you can usually omit `index:` when calling `upsert` / `query` / `text_search`.
|
|
230
|
+
|
|
229
231
|
### Complete Rails Guide
|
|
230
232
|
|
|
231
233
|
For a complete step-by-step guide including:
|
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.1.
|
|
58
|
+
<span class="tma-hero__badge">v1.1.1 — Hybrid Search, Rails Generator, Middleware & Text Search</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/_layouts/page.html
CHANGED
|
@@ -39,6 +39,12 @@
|
|
|
39
39
|
<span class="tma-nav__toggle-line"></span>
|
|
40
40
|
</button>
|
|
41
41
|
<ul class="tma-nav__menu" id="nav-menu">
|
|
42
|
+
<li class="tma-nav__search-wrapper">
|
|
43
|
+
<div class="tma-search">
|
|
44
|
+
<input type="search" id="search-input" class="tma-search__input" placeholder="Search docs..." aria-label="Search documentation">
|
|
45
|
+
<div id="search-results" class="tma-search__results"></div>
|
|
46
|
+
</div>
|
|
47
|
+
</li>
|
|
42
48
|
<li><a href="{{ site.baseurl }}/guides/getting-started" class="tma-nav__link">Getting Started</a></li>
|
|
43
49
|
<li><a href="{{ site.baseurl }}/guides/recipes" class="tma-nav__link">Recipes</a></li>
|
|
44
50
|
<li><a href="{{ site.baseurl }}/providers" class="tma-nav__link">Providers</a></li>
|
|
@@ -122,6 +128,9 @@
|
|
|
122
128
|
</div>
|
|
123
129
|
</footer>
|
|
124
130
|
|
|
131
|
+
<!-- Simple Jekyll Search -->
|
|
132
|
+
<script src="https://cdn.jsdelivr.net/npm/simple-jekyll-search@1.10.0/dest/simple-jekyll-search.min.js"></script>
|
|
133
|
+
|
|
125
134
|
<script>
|
|
126
135
|
document.addEventListener('DOMContentLoaded', function() {
|
|
127
136
|
const navToggle = document.querySelector('.tma-nav__toggle');
|
|
@@ -155,6 +164,19 @@
|
|
|
155
164
|
}
|
|
156
165
|
});
|
|
157
166
|
}
|
|
167
|
+
|
|
168
|
+
// Initialize search
|
|
169
|
+
if (typeof SimpleJekyllSearch !== 'undefined') {
|
|
170
|
+
SimpleJekyllSearch({
|
|
171
|
+
searchInput: document.getElementById('search-input'),
|
|
172
|
+
resultsContainer: document.getElementById('search-results'),
|
|
173
|
+
json: '{{ site.baseurl }}/search.json',
|
|
174
|
+
searchResultTemplate: '<div class="tma-search__result"><a href="{url}" class="tma-search__result-link"><span class="tma-search__result-title">{title}</span><span class="tma-search__result-excerpt">{excerpt}</span></a></div>',
|
|
175
|
+
noResultsText: '<div class="tma-search__no-results">No results found</div>',
|
|
176
|
+
fuzzy: true,
|
|
177
|
+
limit: 10
|
|
178
|
+
});
|
|
179
|
+
}
|
|
158
180
|
});
|
|
159
181
|
</script>
|
|
160
182
|
</body>
|
data/docs/api/cheatsheet.md
CHANGED
|
@@ -49,6 +49,8 @@ client.upsert(vectors: [...])
|
|
|
49
49
|
client.query(vector: query_embedding, top_k: 10)
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
In a Rails app with `config/vectra.yml` generated by `rails generate vectra:index`, if that YAML file contains only one entry, `Vectra::Client.new` will automatically use that entry's `index` (and `namespace`, if present) as defaults.
|
|
53
|
+
|
|
52
54
|
### Upsert
|
|
53
55
|
|
|
54
56
|
```ruby
|
|
@@ -193,6 +195,14 @@ else
|
|
|
193
195
|
end
|
|
194
196
|
```
|
|
195
197
|
|
|
198
|
+
For fast health checks you can temporarily lower the timeout:
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
status = client.with_timeout(0.5) do |c|
|
|
202
|
+
c.ping
|
|
203
|
+
end
|
|
204
|
+
```
|
|
205
|
+
|
|
196
206
|
### Ping (with latency)
|
|
197
207
|
|
|
198
208
|
```ruby
|
data/docs/api/methods.md
CHANGED
|
@@ -36,6 +36,8 @@ client = Vectra::Client.new(
|
|
|
36
36
|
)
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
In a Rails app that uses the `vectra:index` generator, if `config/vectra.yml` contains exactly one entry, `Vectra::Client.new` will automatically use that entry's `index` (and `namespace` if present) as its defaults. This allows you to omit `index:` in most calls (`upsert`, `query`, `text_search`, etc.).
|
|
40
|
+
|
|
39
41
|
---
|
|
40
42
|
|
|
41
43
|
### `client.upsert(index:, vectors:, namespace: nil)`
|
|
@@ -402,6 +404,28 @@ puts "Latency: #{status[:latency_ms]}ms"
|
|
|
402
404
|
|
|
403
405
|
---
|
|
404
406
|
|
|
407
|
+
### `client.with_timeout(seconds) { ... }`
|
|
408
|
+
|
|
409
|
+
Temporarily override the client's request timeout inside a block.
|
|
410
|
+
|
|
411
|
+
**Parameters:**
|
|
412
|
+
- `seconds` (Float) - Temporary timeout in seconds
|
|
413
|
+
|
|
414
|
+
**Returns:** Block result
|
|
415
|
+
|
|
416
|
+
**Example (fast health check in Rails controller):**
|
|
417
|
+
```ruby
|
|
418
|
+
status = client.with_timeout(0.5) do |c|
|
|
419
|
+
c.ping
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
render json: status, status: status[:healthy] ? :ok : :service_unavailable
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
After the block finishes (even if it raises), the previous `config.timeout` value is restored.
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
405
429
|
### `client.health_check`
|
|
406
430
|
|
|
407
431
|
Detailed health check with provider-specific information.
|
data/docs/api/overview.md
CHANGED
|
@@ -10,15 +10,15 @@ permalink: /api/overview/
|
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
12
|
client = Vectra::Client.new(
|
|
13
|
-
provider: :pinecone, # Required: :pinecone, :qdrant, :weaviate, :pgvector
|
|
13
|
+
provider: :pinecone, # Required: :pinecone, :qdrant, :weaviate, :pgvector, :memory
|
|
14
14
|
api_key: 'your-api-key', # Required for cloud providers
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
port: 6333, # For self-hosted providers
|
|
18
|
-
environment: 'us-west-4' # For Pinecone
|
|
15
|
+
index: 'my-index', # Optional default index
|
|
16
|
+
namespace: 'tenant-1' # Optional default namespace
|
|
19
17
|
)
|
|
20
18
|
```
|
|
21
19
|
|
|
20
|
+
In Rails, if you use the `vectra:index` generator and `config/vectra.yml` contains exactly one entry, a plain `Vectra::Client.new` will automatically pick that entry's `index` (and `namespace` if present) as defaults.
|
|
21
|
+
|
|
22
22
|
## Core Methods
|
|
23
23
|
|
|
24
24
|
### `upsert(vectors:)`
|
|
@@ -138,6 +138,31 @@ results = client.hybrid_search(
|
|
|
138
138
|
|
|
139
139
|
**Provider Support:** Qdrant ✅, Weaviate ✅, pgvector ✅, Pinecone ⚠️
|
|
140
140
|
|
|
141
|
+
### `text_search(index:, text:, top_k:)`
|
|
142
|
+
|
|
143
|
+
Text-only search (keyword search without requiring embeddings).
|
|
144
|
+
|
|
145
|
+
**Parameters:**
|
|
146
|
+
- `index` (String) - Index/collection name (uses client's default index when omitted)
|
|
147
|
+
- `text` (String) - Text query for keyword search
|
|
148
|
+
- `top_k` (Integer) - Number of results (default: 10)
|
|
149
|
+
- `namespace` (String, optional) - Namespace
|
|
150
|
+
- `filter` (Hash, optional) - Metadata filter
|
|
151
|
+
- `include_values` (Boolean) - Include vector values (default: false)
|
|
152
|
+
- `include_metadata` (Boolean) - Include metadata (default: true)
|
|
153
|
+
|
|
154
|
+
**Example:**
|
|
155
|
+
```ruby
|
|
156
|
+
results = client.text_search(
|
|
157
|
+
index: 'products',
|
|
158
|
+
text: 'iPhone 15 Pro',
|
|
159
|
+
top_k: 10,
|
|
160
|
+
filter: { category: 'electronics' }
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Provider Support:** Qdrant ✅ (BM25), Weaviate ✅ (BM25), pgvector ✅ (PostgreSQL full-text), Memory ✅, Pinecone ❌
|
|
165
|
+
|
|
141
166
|
### `healthy?`
|
|
142
167
|
|
|
143
168
|
Quick health check - returns true if provider connection is healthy.
|
|
@@ -151,6 +176,12 @@ if client.healthy?
|
|
|
151
176
|
end
|
|
152
177
|
```
|
|
153
178
|
|
|
179
|
+
You can also run faster checks with a temporary timeout:
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
fast_ok = client.with_timeout(0.5) { |c| c.healthy? }
|
|
183
|
+
```
|
|
184
|
+
|
|
154
185
|
### `ping`
|
|
155
186
|
|
|
156
187
|
Ping provider and get connection health status with latency.
|
data/docs/assets/style.css
CHANGED
|
@@ -165,6 +165,118 @@ body {
|
|
|
165
165
|
border-color: var(--tma-color-border-hover);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
.tma-nav__search-wrapper {
|
|
169
|
+
position: relative;
|
|
170
|
+
margin-right: var(--tma-spacing-md);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.tma-search {
|
|
174
|
+
position: relative;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.tma-search__input {
|
|
178
|
+
width: 240px;
|
|
179
|
+
padding: var(--tma-spacing-sm) var(--tma-spacing-md);
|
|
180
|
+
padding-left: 2.5rem;
|
|
181
|
+
background: var(--tma-color-bg-tertiary);
|
|
182
|
+
border: 1px solid var(--tma-color-border);
|
|
183
|
+
border-radius: var(--tma-radius-sm);
|
|
184
|
+
color: var(--tma-color-text-primary);
|
|
185
|
+
font-size: 0.9rem;
|
|
186
|
+
font-family: var(--tma-font-family-primary);
|
|
187
|
+
transition: all var(--tma-transition-fast);
|
|
188
|
+
outline: none;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.tma-search__input::placeholder {
|
|
192
|
+
color: var(--tma-color-text-muted);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.tma-search__input:focus {
|
|
196
|
+
width: 320px;
|
|
197
|
+
border-color: var(--tma-color-accent-primary);
|
|
198
|
+
background: var(--tma-color-bg-elevated);
|
|
199
|
+
box-shadow: 0 0 0 3px var(--tma-color-accent-muted);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.tma-search {
|
|
203
|
+
position: relative;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.tma-search::before {
|
|
207
|
+
content: '🔍';
|
|
208
|
+
position: absolute;
|
|
209
|
+
left: var(--tma-spacing-md);
|
|
210
|
+
top: 50%;
|
|
211
|
+
transform: translateY(-50%);
|
|
212
|
+
color: var(--tma-color-text-muted);
|
|
213
|
+
pointer-events: none;
|
|
214
|
+
z-index: 1;
|
|
215
|
+
font-size: 0.9rem;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.tma-search__results {
|
|
219
|
+
position: absolute;
|
|
220
|
+
top: calc(100% + var(--tma-spacing-xs));
|
|
221
|
+
left: 0;
|
|
222
|
+
right: 0;
|
|
223
|
+
max-width: 500px;
|
|
224
|
+
max-height: 400px;
|
|
225
|
+
overflow-y: auto;
|
|
226
|
+
background: var(--tma-color-bg-elevated);
|
|
227
|
+
border: 1px solid var(--tma-color-border);
|
|
228
|
+
border-radius: var(--tma-radius-md);
|
|
229
|
+
box-shadow: var(--tma-shadow-lg);
|
|
230
|
+
z-index: 1000;
|
|
231
|
+
display: none;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.tma-search__results:not(:empty) {
|
|
235
|
+
display: block;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.tma-search__result {
|
|
239
|
+
border-bottom: 1px solid var(--tma-color-border);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.tma-search__result:last-child {
|
|
243
|
+
border-bottom: none;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.tma-search__result-link {
|
|
247
|
+
display: block;
|
|
248
|
+
padding: var(--tma-spacing-md);
|
|
249
|
+
text-decoration: none;
|
|
250
|
+
color: var(--tma-color-text-primary);
|
|
251
|
+
transition: background var(--tma-transition-fast);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.tma-search__result-link:hover {
|
|
255
|
+
background: var(--tma-color-bg-hover);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.tma-search__result-title {
|
|
259
|
+
display: block;
|
|
260
|
+
font-weight: 600;
|
|
261
|
+
font-size: 0.95rem;
|
|
262
|
+
color: var(--tma-color-text-primary);
|
|
263
|
+
margin-bottom: var(--tma-spacing-xs);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.tma-search__result-excerpt {
|
|
267
|
+
display: block;
|
|
268
|
+
font-size: 0.85rem;
|
|
269
|
+
color: var(--tma-color-text-secondary);
|
|
270
|
+
line-height: 1.5;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.tma-search__no-results {
|
|
274
|
+
padding: var(--tma-spacing-lg);
|
|
275
|
+
text-align: center;
|
|
276
|
+
color: var(--tma-color-text-muted);
|
|
277
|
+
font-size: 0.9rem;
|
|
278
|
+
}
|
|
279
|
+
|
|
168
280
|
.tma-nav__toggle {
|
|
169
281
|
display: none;
|
|
170
282
|
flex-direction: column;
|
|
@@ -1261,6 +1373,25 @@ code {
|
|
|
1261
1373
|
justify-content: center;
|
|
1262
1374
|
}
|
|
1263
1375
|
|
|
1376
|
+
.tma-nav__search-wrapper {
|
|
1377
|
+
width: 100%;
|
|
1378
|
+
margin-right: 0;
|
|
1379
|
+
margin-bottom: var(--tma-spacing-md);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
.tma-search__input {
|
|
1383
|
+
width: 100%;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
.tma-search__input:focus {
|
|
1387
|
+
width: 100%;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
.tma-search__results {
|
|
1391
|
+
max-width: 100%;
|
|
1392
|
+
right: 0;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1264
1395
|
.tma-features,
|
|
1265
1396
|
.tma-providers {
|
|
1266
1397
|
padding: var(--tma-spacing-xl) var(--tma-spacing-md);
|
|
@@ -99,6 +99,8 @@ This will:
|
|
|
99
99
|
- Update `app/models/product.rb` to include the concern
|
|
100
100
|
- Add configuration to `config/vectra.yml`
|
|
101
101
|
|
|
102
|
+
When `config/vectra.yml` contains exactly one entry, a plain `Vectra::Client.new` in this Rails app will automatically use that entry's `index` (and `namespace` if present) as its defaults. That means you can usually omit `index:` when calling `upsert`, `query`, `hybrid_search`, or `text_search`.
|
|
103
|
+
|
|
102
104
|
### Run Migrations
|
|
103
105
|
|
|
104
106
|
```bash
|
data/docs/search.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: null
|
|
3
|
+
---
|
|
4
|
+
[
|
|
5
|
+
{% assign first = true %}
|
|
6
|
+
{% for page in site.pages %}
|
|
7
|
+
{% unless page.url == '/' or page.url == '/search.json' or page.url contains '/assets/' or page.url contains '/404' or page.url contains '/feed' or page.url contains '/sitemap' or page.url contains '/robots' or page.url contains '/index.html' or page.url contains '/index.md' %}
|
|
8
|
+
{% unless first %},{% endunless %}
|
|
9
|
+
{
|
|
10
|
+
"title": {{ page.title | default: page.url | jsonify }},
|
|
11
|
+
"url": {{ page.url | jsonify }},
|
|
12
|
+
"excerpt": {{ page.content | strip_html | truncatewords: 30 | default: "" | jsonify }}
|
|
13
|
+
}
|
|
14
|
+
{% assign first = false %}
|
|
15
|
+
{% endunless %}
|
|
16
|
+
{% endfor %}
|
|
17
|
+
{% for post in site.posts %}
|
|
18
|
+
{% unless first %},{% endunless %}
|
|
19
|
+
{
|
|
20
|
+
"title": {{ post.title | jsonify }},
|
|
21
|
+
"url": {{ post.url | jsonify }},
|
|
22
|
+
"excerpt": {{ post.content | strip_html | truncatewords: 30 | default: "" | jsonify }}
|
|
23
|
+
}
|
|
24
|
+
{% assign first = false %}
|
|
25
|
+
{% endfor %}
|
|
26
|
+
]
|
data/lib/vectra/client.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
3
5
|
# Ensure HealthCheck is loaded before Client
|
|
4
6
|
require_relative "health_check" unless defined?(Vectra::HealthCheck)
|
|
5
7
|
require_relative "configuration" unless defined?(Vectra::Configuration)
|
|
@@ -89,6 +91,7 @@ module Vectra
|
|
|
89
91
|
@provider = build_provider
|
|
90
92
|
@default_index = options[:index]
|
|
91
93
|
@default_namespace = options[:namespace]
|
|
94
|
+
apply_rails_vectra_defaults! if @default_index.nil? && @default_namespace.nil?
|
|
92
95
|
@middleware = build_middleware_stack(options[:middleware])
|
|
93
96
|
end
|
|
94
97
|
|
|
@@ -745,6 +748,44 @@ module Vectra
|
|
|
745
748
|
Middleware::Stack.new(@provider, all_middleware)
|
|
746
749
|
end
|
|
747
750
|
|
|
751
|
+
def apply_rails_vectra_defaults!
|
|
752
|
+
return unless rails_root_available?
|
|
753
|
+
|
|
754
|
+
entry = load_single_vectra_entry
|
|
755
|
+
return unless entry
|
|
756
|
+
|
|
757
|
+
apply_vectra_defaults_from(entry)
|
|
758
|
+
rescue StandardError => e
|
|
759
|
+
log_error("Failed to infer default index/namespace from config/vectra.yml", e)
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
def rails_root_available?
|
|
763
|
+
defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
def vectra_config_path
|
|
767
|
+
File.join(Rails.root.to_s, "config", "vectra.yml")
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def load_single_vectra_entry
|
|
771
|
+
path = vectra_config_path
|
|
772
|
+
return unless File.exist?(path)
|
|
773
|
+
|
|
774
|
+
raw = File.read(path)
|
|
775
|
+
data = YAML.safe_load(raw, permitted_classes: [], aliases: true) || {}
|
|
776
|
+
return unless data.is_a?(Hash) && data.size == 1
|
|
777
|
+
|
|
778
|
+
data.values.first || {}
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
def apply_vectra_defaults_from(entry)
|
|
782
|
+
index = entry["index"] || entry[:index]
|
|
783
|
+
namespace = entry["namespace"] || entry[:namespace]
|
|
784
|
+
|
|
785
|
+
@default_index = index if @default_index.nil? && index.is_a?(String) && !index.empty?
|
|
786
|
+
@default_namespace = namespace if @default_namespace.nil? && namespace.is_a?(String) && !namespace.empty?
|
|
787
|
+
end
|
|
788
|
+
|
|
748
789
|
def validate_index!(index)
|
|
749
790
|
raise ValidationError, "Index name cannot be nil" if index.nil?
|
|
750
791
|
raise ValidationError, "Index name must be a string" unless index.is_a?(String)
|
|
@@ -811,6 +852,22 @@ module Vectra
|
|
|
811
852
|
config.logger.debug("[Vectra] #{data.inspect}") if data
|
|
812
853
|
end
|
|
813
854
|
|
|
855
|
+
# Temporarily override request timeout within a block.
|
|
856
|
+
#
|
|
857
|
+
# This updates the client's configuration timeout for the duration
|
|
858
|
+
# of the block and then restores the previous value.
|
|
859
|
+
#
|
|
860
|
+
# @param seconds [Float] temporary timeout in seconds
|
|
861
|
+
# @yield [Client] yields self with overridden timeout
|
|
862
|
+
# @return [Object] block result
|
|
863
|
+
def with_timeout(seconds)
|
|
864
|
+
previous = config.timeout
|
|
865
|
+
config.timeout = seconds
|
|
866
|
+
yield self
|
|
867
|
+
ensure
|
|
868
|
+
config.timeout = previous
|
|
869
|
+
end
|
|
870
|
+
|
|
814
871
|
# Temporarily override default index within a block.
|
|
815
872
|
#
|
|
816
873
|
# @param index [String] temporary index name
|
|
@@ -851,7 +908,7 @@ module Vectra
|
|
|
851
908
|
end
|
|
852
909
|
end
|
|
853
910
|
|
|
854
|
-
public :with_index, :with_namespace, :with_index_and_namespace
|
|
911
|
+
public :with_index, :with_namespace, :with_index_and_namespace, :with_timeout
|
|
855
912
|
end
|
|
856
913
|
# rubocop:enable Metrics/ClassLength
|
|
857
914
|
end
|
data/lib/vectra/health_check.rb
CHANGED
|
@@ -31,7 +31,7 @@ module Vectra
|
|
|
31
31
|
|
|
32
32
|
# For health checks we bypass client middleware and call the provider
|
|
33
33
|
# directly to avoid interference from custom stacks.
|
|
34
|
-
indexes =
|
|
34
|
+
indexes = healthcheck_with_timeout(timeout) { provider.list_indexes }
|
|
35
35
|
index_name = index || indexes.first&.dig(:name)
|
|
36
36
|
|
|
37
37
|
result = base_result(start_time, indexes)
|
|
@@ -53,7 +53,7 @@ module Vectra
|
|
|
53
53
|
|
|
54
54
|
private
|
|
55
55
|
|
|
56
|
-
def
|
|
56
|
+
def healthcheck_with_timeout(seconds, &)
|
|
57
57
|
Timeout.timeout(seconds, &)
|
|
58
58
|
rescue Timeout::Error
|
|
59
59
|
raise Vectra::TimeoutError, "Health check timed out after #{seconds}s"
|
|
@@ -72,7 +72,7 @@ module Vectra
|
|
|
72
72
|
def add_index_stats(result, index_name, include_stats, timeout)
|
|
73
73
|
return unless include_stats && index_name
|
|
74
74
|
|
|
75
|
-
stats =
|
|
75
|
+
stats = healthcheck_with_timeout(timeout) { provider.stats(index: index_name) }
|
|
76
76
|
result[:index] = index_name
|
|
77
77
|
result[:stats] = {
|
|
78
78
|
vector_count: stats[:total_vector_count],
|
data/lib/vectra/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vectra-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mijo Kristo
|
|
@@ -289,6 +289,7 @@ files:
|
|
|
289
289
|
- docs/providers/qdrant.md
|
|
290
290
|
- docs/providers/selection.md
|
|
291
291
|
- docs/providers/weaviate.md
|
|
292
|
+
- docs/search.json
|
|
292
293
|
- examples/GRAFANA_QUICKSTART.md
|
|
293
294
|
- examples/README.md
|
|
294
295
|
- examples/active_record_demo.rb
|