@fluxsoft/fluxvector 0.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.
- package/README.md +234 -0
- package/dist/index.cjs +495 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +337 -0
- package/dist/index.d.ts +337 -0
- package/dist/index.js +480 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# @fluxsoft/fluxvector
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for [FluxVector](https://fluxsoftlabs.com/fluxvector) — semantic search API by FluxSoft Labs.
|
|
4
|
+
|
|
5
|
+
- Zero runtime dependencies (native `fetch`, works in Node 18+, Deno, Bun, edge runtimes)
|
|
6
|
+
- Full TypeScript types for every request and response
|
|
7
|
+
- Automatic retry with exponential backoff
|
|
8
|
+
- Auto-chunking for large upserts
|
|
9
|
+
- ESM + CJS dual package
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @fluxsoft/fluxvector
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @fluxsoft/fluxvector
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
yarn add @fluxsoft/fluxvector
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { FluxVector } from '@fluxsoft/fluxvector';
|
|
29
|
+
|
|
30
|
+
const fv = new FluxVector({ apiKey: 'fv_live_abc123' });
|
|
31
|
+
|
|
32
|
+
// Create a collection
|
|
33
|
+
await fv.collections.create({ name: 'products' });
|
|
34
|
+
|
|
35
|
+
// Upsert vectors
|
|
36
|
+
await fv.vectors.upsert('products', [
|
|
37
|
+
{ id: 'p1', text: 'Red shoes', metadata: { price: 89 } },
|
|
38
|
+
{ id: 'p2', text: 'Blue sneakers', metadata: { price: 120 } },
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
// Search
|
|
42
|
+
const results = await fv.search('products', 'comfortable shoes', {
|
|
43
|
+
topK: 5,
|
|
44
|
+
filter: { price: { $lt: 100 } },
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
for (const r of results) {
|
|
48
|
+
console.log(`${r.id}: ${r.score.toFixed(2)} — ${r.text}`);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const fv = new FluxVector({
|
|
56
|
+
apiKey: 'fv_live_abc123', // Required
|
|
57
|
+
baseUrl: 'https://fluxvector.dev', // Default, configurable for self-hosted
|
|
58
|
+
maxRetries: 3, // Default: 3 (retries on 429 and 5xx)
|
|
59
|
+
timeout: 30000, // Default: 30s
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## API Reference
|
|
64
|
+
|
|
65
|
+
### Collections
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// Create
|
|
69
|
+
const col = await fv.collections.create({
|
|
70
|
+
name: 'products',
|
|
71
|
+
dimension: 1536, // Optional, default: auto
|
|
72
|
+
metric: 'cosine', // 'cosine' | 'euclidean' | 'dotproduct'
|
|
73
|
+
description: 'Product catalog',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// List (cursor pagination)
|
|
77
|
+
const { data, has_more, next_cursor } = await fv.collections.list({ limit: 10 });
|
|
78
|
+
|
|
79
|
+
// Get
|
|
80
|
+
const col = await fv.collections.get('products');
|
|
81
|
+
|
|
82
|
+
// Delete
|
|
83
|
+
await fv.collections.delete('products');
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Vectors
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// Upsert (auto-chunks at 1000 vectors)
|
|
90
|
+
await fv.vectors.upsert('products', [
|
|
91
|
+
{ id: 'p1', text: 'Red shoes', metadata: { price: 89, category: 'footwear' } },
|
|
92
|
+
{ id: 'p2', values: [0.1, 0.2, ...], metadata: { price: 120 } },
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
// Query
|
|
96
|
+
const { results, usage, took_ms } = await fv.vectors.query({
|
|
97
|
+
collection: 'products',
|
|
98
|
+
text: 'comfortable shoes',
|
|
99
|
+
top_k: 10,
|
|
100
|
+
filter: { category: { $eq: 'footwear' } },
|
|
101
|
+
include_metadata: true,
|
|
102
|
+
include_text: true,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Fetch by IDs
|
|
106
|
+
const { vectors } = await fv.vectors.fetch('products', ['p1', 'p2']);
|
|
107
|
+
|
|
108
|
+
// Delete by IDs
|
|
109
|
+
await fv.vectors.delete('products', { ids: ['p1', 'p2'] });
|
|
110
|
+
|
|
111
|
+
// Delete by filter
|
|
112
|
+
await fv.vectors.delete('products', { filter: { category: { $eq: 'old' } } });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Search
|
|
116
|
+
|
|
117
|
+
The top-level `fv.search()` is the simplest way to do semantic search:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// Returns QueryResult[] directly
|
|
121
|
+
const results = await fv.search('products', 'comfortable shoes', {
|
|
122
|
+
topK: 5,
|
|
123
|
+
filter: { price: { $lt: 100 } },
|
|
124
|
+
mode: 'hybrid',
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Each result: { id, score, text?, metadata? }
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Embeddings
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Single text
|
|
134
|
+
const { embedding, dimension, model } = await fv.embeddings.create('hello world');
|
|
135
|
+
|
|
136
|
+
// Batch
|
|
137
|
+
const { embeddings, dimension, model } = await fv.embeddings.createBatch([
|
|
138
|
+
'hello world',
|
|
139
|
+
'goodbye world',
|
|
140
|
+
]);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### API Keys
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// Create
|
|
147
|
+
const key = await fv.apiKeys.create({ name: 'Production', env: 'live' });
|
|
148
|
+
console.log(key.key); // fv_live_xxx — shown only once
|
|
149
|
+
|
|
150
|
+
// List
|
|
151
|
+
const { data } = await fv.apiKeys.list();
|
|
152
|
+
|
|
153
|
+
// Update
|
|
154
|
+
await fv.apiKeys.update('key_123', { name: 'Renamed' });
|
|
155
|
+
|
|
156
|
+
// Delete
|
|
157
|
+
await fv.apiKeys.delete('key_123');
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Usage
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// Current period
|
|
164
|
+
const usage = await fv.usage.get();
|
|
165
|
+
// { plan, period_start, requests, embeddings, vectors_stored, collections }
|
|
166
|
+
|
|
167
|
+
// History
|
|
168
|
+
const { data } = await fv.usage.history({ days: 30 });
|
|
169
|
+
// [{ date, requests, embeddings, vectors }]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Filter Operators
|
|
173
|
+
|
|
174
|
+
Filters use MongoDB-style operators on metadata fields:
|
|
175
|
+
|
|
176
|
+
| Operator | Description |
|
|
177
|
+
|----------|-------------|
|
|
178
|
+
| `$eq` | Equal to |
|
|
179
|
+
| `$ne` | Not equal to |
|
|
180
|
+
| `$gt` | Greater than |
|
|
181
|
+
| `$gte` | Greater than or equal |
|
|
182
|
+
| `$lt` | Less than |
|
|
183
|
+
| `$lte` | Less than or equal |
|
|
184
|
+
| `$in` | In array |
|
|
185
|
+
| `$nin` | Not in array |
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// Examples
|
|
189
|
+
{ price: { $lt: 100 } }
|
|
190
|
+
{ category: { $in: ['shoes', 'boots'] } }
|
|
191
|
+
{ status: { $ne: 'archived' } }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Error Handling
|
|
195
|
+
|
|
196
|
+
All errors extend `FluxVectorError` with `status`, `code`, and optional `requestId`:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import { FluxVector, AuthenticationError, NotFoundError, RateLimitError } from '@fluxsoft/fluxvector';
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
await fv.collections.get('missing');
|
|
203
|
+
} catch (err) {
|
|
204
|
+
if (err instanceof NotFoundError) {
|
|
205
|
+
console.log('Collection does not exist');
|
|
206
|
+
} else if (err instanceof AuthenticationError) {
|
|
207
|
+
console.log('Check your API key');
|
|
208
|
+
} else if (err instanceof RateLimitError) {
|
|
209
|
+
console.log(`Retry after ${err.retryAfter}s`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
| Error Class | Status | When |
|
|
215
|
+
|-------------|--------|------|
|
|
216
|
+
| `AuthenticationError` | 401 | Invalid or missing API key |
|
|
217
|
+
| `NotFoundError` | 404 | Resource not found |
|
|
218
|
+
| `ValidationError` | 422 | Invalid request body |
|
|
219
|
+
| `RateLimitError` | 429 | Too many requests |
|
|
220
|
+
| `ServerError` | 5xx | Server-side error |
|
|
221
|
+
| `FluxVectorError` | any | Base class for all errors |
|
|
222
|
+
|
|
223
|
+
## Retry Behavior
|
|
224
|
+
|
|
225
|
+
The SDK automatically retries on 429 (rate limit) and 5xx (server errors):
|
|
226
|
+
|
|
227
|
+
- Default: 3 retries with exponential backoff (500ms, 1s, 2s + jitter)
|
|
228
|
+
- Respects `Retry-After` headers on 429 responses
|
|
229
|
+
- Non-retryable errors (400, 401, 404, 422) fail immediately
|
|
230
|
+
- Set `maxRetries: 0` to disable retries
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var FluxVectorError = class extends Error {
|
|
5
|
+
status;
|
|
6
|
+
code;
|
|
7
|
+
requestId;
|
|
8
|
+
constructor(message, status, code = "unknown_error", requestId) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "FluxVectorError";
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.requestId = requestId;
|
|
14
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var AuthenticationError = class extends FluxVectorError {
|
|
18
|
+
constructor(message = "Invalid or missing API key", requestId) {
|
|
19
|
+
super(message, 401, "authentication_error", requestId);
|
|
20
|
+
this.name = "AuthenticationError";
|
|
21
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var NotFoundError = class extends FluxVectorError {
|
|
25
|
+
constructor(message = "Resource not found", requestId) {
|
|
26
|
+
super(message, 404, "not_found", requestId);
|
|
27
|
+
this.name = "NotFoundError";
|
|
28
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var RateLimitError = class extends FluxVectorError {
|
|
32
|
+
retryAfter;
|
|
33
|
+
constructor(message = "Rate limit exceeded", retryAfter, requestId) {
|
|
34
|
+
super(message, 429, "rate_limit_exceeded", requestId);
|
|
35
|
+
this.name = "RateLimitError";
|
|
36
|
+
this.retryAfter = retryAfter;
|
|
37
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var ValidationError = class extends FluxVectorError {
|
|
41
|
+
constructor(message = "Validation failed", requestId) {
|
|
42
|
+
super(message, 422, "validation_error", requestId);
|
|
43
|
+
this.name = "ValidationError";
|
|
44
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var ServerError = class extends FluxVectorError {
|
|
48
|
+
constructor(message = "Internal server error", status = 500, requestId) {
|
|
49
|
+
super(message, status, "server_error", requestId);
|
|
50
|
+
this.name = "ServerError";
|
|
51
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// src/client.ts
|
|
56
|
+
var DEFAULT_BASE_URL = "https://fluxvector.dev";
|
|
57
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
58
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
59
|
+
var RETRY_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
60
|
+
var HttpClient = class {
|
|
61
|
+
config;
|
|
62
|
+
constructor(config) {
|
|
63
|
+
this.config = {
|
|
64
|
+
apiKey: config.apiKey,
|
|
65
|
+
baseUrl: (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
|
|
66
|
+
maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
67
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async request(options) {
|
|
71
|
+
const url = this.buildUrl(options.path, options.query);
|
|
72
|
+
const headers = {
|
|
73
|
+
"Authorization": `Bearer ${this.config.apiKey}`,
|
|
74
|
+
"Content-Type": "application/json",
|
|
75
|
+
"User-Agent": "@fluxsoft/fluxvector/0.1.0"
|
|
76
|
+
};
|
|
77
|
+
const init = {
|
|
78
|
+
method: options.method,
|
|
79
|
+
headers,
|
|
80
|
+
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
81
|
+
signal: AbortSignal.timeout(this.config.timeout)
|
|
82
|
+
};
|
|
83
|
+
let lastError;
|
|
84
|
+
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
85
|
+
if (attempt > 0) {
|
|
86
|
+
await this.sleep(this.getRetryDelay(attempt, lastError));
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(url, init);
|
|
90
|
+
if (response.ok) {
|
|
91
|
+
return await response.json();
|
|
92
|
+
}
|
|
93
|
+
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
94
|
+
if (!RETRY_STATUS_CODES.has(response.status) || attempt === this.config.maxRetries) {
|
|
95
|
+
throw this.buildError(response.status, await this.safeJson(response), requestId);
|
|
96
|
+
}
|
|
97
|
+
lastError = this.buildError(response.status, await this.safeJson(response), requestId);
|
|
98
|
+
if (response.status === 429) {
|
|
99
|
+
const retryAfter = response.headers.get("retry-after");
|
|
100
|
+
if (retryAfter && lastError instanceof RateLimitError) {
|
|
101
|
+
lastError.retryAfter = Number(retryAfter);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error instanceof FluxVectorError) {
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
if (attempt === this.config.maxRetries) {
|
|
109
|
+
if (error instanceof DOMException && error.name === "TimeoutError") {
|
|
110
|
+
throw new FluxVectorError("Request timed out", 0, "timeout");
|
|
111
|
+
}
|
|
112
|
+
throw new FluxVectorError(
|
|
113
|
+
error instanceof Error ? error.message : "Unknown network error",
|
|
114
|
+
0,
|
|
115
|
+
"network_error"
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
throw lastError ?? new FluxVectorError("Request failed after retries", 0, "retry_exhausted");
|
|
122
|
+
}
|
|
123
|
+
buildUrl(path, query) {
|
|
124
|
+
const url = new URL(`${this.config.baseUrl}${path}`);
|
|
125
|
+
if (query) {
|
|
126
|
+
for (const [key, value] of Object.entries(query)) {
|
|
127
|
+
if (value !== void 0) {
|
|
128
|
+
url.searchParams.set(key, String(value));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return url.toString();
|
|
133
|
+
}
|
|
134
|
+
buildError(status, body, requestId) {
|
|
135
|
+
const message = body?.message ?? body?.error ?? `Request failed with status ${status}`;
|
|
136
|
+
switch (status) {
|
|
137
|
+
case 401:
|
|
138
|
+
return new AuthenticationError(message, requestId);
|
|
139
|
+
case 404:
|
|
140
|
+
return new NotFoundError(message, requestId);
|
|
141
|
+
case 422:
|
|
142
|
+
return new ValidationError(message, requestId);
|
|
143
|
+
case 429: {
|
|
144
|
+
const retryAfter = body?.retry_after;
|
|
145
|
+
return new RateLimitError(message, retryAfter, requestId);
|
|
146
|
+
}
|
|
147
|
+
default:
|
|
148
|
+
if (status >= 500) {
|
|
149
|
+
return new ServerError(message, status, requestId);
|
|
150
|
+
}
|
|
151
|
+
return new FluxVectorError(message, status, "api_error", requestId);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async safeJson(response) {
|
|
155
|
+
try {
|
|
156
|
+
return await response.json();
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
getRetryDelay(attempt, lastError) {
|
|
162
|
+
if (lastError instanceof RateLimitError && lastError.retryAfter) {
|
|
163
|
+
return lastError.retryAfter * 1e3;
|
|
164
|
+
}
|
|
165
|
+
const base = Math.min(500 * Math.pow(2, attempt - 1), 4e3);
|
|
166
|
+
const jitter = Math.random() * base * 0.5;
|
|
167
|
+
return base + jitter;
|
|
168
|
+
}
|
|
169
|
+
sleep(ms) {
|
|
170
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// src/resources/collections.ts
|
|
175
|
+
var Collections = class {
|
|
176
|
+
constructor(client) {
|
|
177
|
+
this.client = client;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Create a new collection.
|
|
181
|
+
*/
|
|
182
|
+
async create(params) {
|
|
183
|
+
return this.client.request({
|
|
184
|
+
method: "POST",
|
|
185
|
+
path: "/v1/collections",
|
|
186
|
+
body: params
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* List all collections with cursor-based pagination.
|
|
191
|
+
*/
|
|
192
|
+
async list(params) {
|
|
193
|
+
return this.client.request({
|
|
194
|
+
method: "GET",
|
|
195
|
+
path: "/v1/collections",
|
|
196
|
+
query: {
|
|
197
|
+
cursor: params?.cursor,
|
|
198
|
+
limit: params?.limit
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Get a collection by name.
|
|
204
|
+
*/
|
|
205
|
+
async get(name) {
|
|
206
|
+
return this.client.request({
|
|
207
|
+
method: "GET",
|
|
208
|
+
path: `/v1/collections/${encodeURIComponent(name)}`
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Delete a collection by name.
|
|
213
|
+
*/
|
|
214
|
+
async delete(name) {
|
|
215
|
+
return this.client.request({
|
|
216
|
+
method: "DELETE",
|
|
217
|
+
path: `/v1/collections/${encodeURIComponent(name)}`
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/resources/vectors.ts
|
|
223
|
+
var MAX_UPSERT_BATCH_SIZE = 1e3;
|
|
224
|
+
var Vectors = class {
|
|
225
|
+
constructor(client) {
|
|
226
|
+
this.client = client;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Upsert vectors into a collection.
|
|
230
|
+
* Automatically chunks into batches of 1000 vectors.
|
|
231
|
+
*/
|
|
232
|
+
async upsert(collection, vectors) {
|
|
233
|
+
if (vectors.length <= MAX_UPSERT_BATCH_SIZE) {
|
|
234
|
+
return this.client.request({
|
|
235
|
+
method: "POST",
|
|
236
|
+
path: "/v1/vectors/upsert",
|
|
237
|
+
body: { collection, vectors }
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
let totalUpserted = 0;
|
|
241
|
+
for (let i = 0; i < vectors.length; i += MAX_UPSERT_BATCH_SIZE) {
|
|
242
|
+
const chunk = vectors.slice(i, i + MAX_UPSERT_BATCH_SIZE);
|
|
243
|
+
const result = await this.client.request({
|
|
244
|
+
method: "POST",
|
|
245
|
+
path: "/v1/vectors/upsert",
|
|
246
|
+
body: { collection, vectors: chunk }
|
|
247
|
+
});
|
|
248
|
+
totalUpserted += result.upserted;
|
|
249
|
+
}
|
|
250
|
+
return { upserted: totalUpserted };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Query vectors by text or vector with optional filtering.
|
|
254
|
+
*/
|
|
255
|
+
async query(params) {
|
|
256
|
+
return this.client.request({
|
|
257
|
+
method: "POST",
|
|
258
|
+
path: "/v1/vectors/query",
|
|
259
|
+
body: {
|
|
260
|
+
collection: params.collection,
|
|
261
|
+
text: params.text,
|
|
262
|
+
vector: params.vector,
|
|
263
|
+
top_k: params.top_k,
|
|
264
|
+
filter: params.filter,
|
|
265
|
+
include_metadata: params.include_metadata,
|
|
266
|
+
include_text: params.include_text,
|
|
267
|
+
mode: params.mode
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Delete vectors by IDs or filter.
|
|
273
|
+
*/
|
|
274
|
+
async delete(collection, options) {
|
|
275
|
+
return this.client.request({
|
|
276
|
+
method: "POST",
|
|
277
|
+
path: "/v1/vectors/delete",
|
|
278
|
+
body: {
|
|
279
|
+
collection,
|
|
280
|
+
ids: options.ids,
|
|
281
|
+
filter: options.filter
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Fetch vectors by their IDs.
|
|
287
|
+
*/
|
|
288
|
+
async fetch(collection, ids) {
|
|
289
|
+
return this.client.request({
|
|
290
|
+
method: "GET",
|
|
291
|
+
path: "/v1/vectors/fetch",
|
|
292
|
+
query: {
|
|
293
|
+
collection,
|
|
294
|
+
ids: ids.join(",")
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// src/resources/search.ts
|
|
301
|
+
var Search = class {
|
|
302
|
+
constructor(client) {
|
|
303
|
+
this.client = client;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Semantic search across a collection.
|
|
307
|
+
* Returns matching results ranked by relevance.
|
|
308
|
+
*/
|
|
309
|
+
async query(collection, text, options) {
|
|
310
|
+
const response = await this.client.request({
|
|
311
|
+
method: "POST",
|
|
312
|
+
path: "/v1/search",
|
|
313
|
+
body: {
|
|
314
|
+
collection,
|
|
315
|
+
text,
|
|
316
|
+
top_k: options?.topK,
|
|
317
|
+
filter: options?.filter,
|
|
318
|
+
mode: options?.mode
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
return response.results;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Semantic search with full response (includes usage and timing).
|
|
325
|
+
*/
|
|
326
|
+
async queryWithMeta(collection, text, options) {
|
|
327
|
+
return this.client.request({
|
|
328
|
+
method: "POST",
|
|
329
|
+
path: "/v1/search",
|
|
330
|
+
body: {
|
|
331
|
+
collection,
|
|
332
|
+
text,
|
|
333
|
+
top_k: options?.topK,
|
|
334
|
+
filter: options?.filter,
|
|
335
|
+
mode: options?.mode
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// src/resources/embeddings.ts
|
|
342
|
+
var Embeddings = class {
|
|
343
|
+
constructor(client) {
|
|
344
|
+
this.client = client;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Generate an embedding for a single text.
|
|
348
|
+
*/
|
|
349
|
+
async create(text) {
|
|
350
|
+
return this.client.request({
|
|
351
|
+
method: "POST",
|
|
352
|
+
path: "/v1/embed",
|
|
353
|
+
body: { text }
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Generate embeddings for multiple texts in a single request.
|
|
358
|
+
*/
|
|
359
|
+
async createBatch(texts) {
|
|
360
|
+
return this.client.request({
|
|
361
|
+
method: "POST",
|
|
362
|
+
path: "/v1/embed/batch",
|
|
363
|
+
body: { texts }
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// src/resources/api-keys.ts
|
|
369
|
+
var ApiKeys = class {
|
|
370
|
+
constructor(client) {
|
|
371
|
+
this.client = client;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Create a new API key.
|
|
375
|
+
*/
|
|
376
|
+
async create(params) {
|
|
377
|
+
return this.client.request({
|
|
378
|
+
method: "POST",
|
|
379
|
+
path: "/v1/api-keys",
|
|
380
|
+
body: params
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* List all API keys.
|
|
385
|
+
*/
|
|
386
|
+
async list() {
|
|
387
|
+
return this.client.request({
|
|
388
|
+
method: "GET",
|
|
389
|
+
path: "/v1/api-keys"
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Update an API key's name.
|
|
394
|
+
*/
|
|
395
|
+
async update(id, params) {
|
|
396
|
+
return this.client.request({
|
|
397
|
+
method: "PATCH",
|
|
398
|
+
path: `/v1/api-keys/${encodeURIComponent(id)}`,
|
|
399
|
+
body: params
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Delete an API key.
|
|
404
|
+
*/
|
|
405
|
+
async delete(id) {
|
|
406
|
+
return this.client.request({
|
|
407
|
+
method: "DELETE",
|
|
408
|
+
path: `/v1/api-keys/${encodeURIComponent(id)}`
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// src/resources/usage.ts
|
|
414
|
+
var Usage = class {
|
|
415
|
+
constructor(client) {
|
|
416
|
+
this.client = client;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Get current usage overview for the billing period.
|
|
420
|
+
*/
|
|
421
|
+
async get() {
|
|
422
|
+
return this.client.request({
|
|
423
|
+
method: "GET",
|
|
424
|
+
path: "/v1/usage"
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Get historical usage data.
|
|
429
|
+
*/
|
|
430
|
+
async history(params) {
|
|
431
|
+
return this.client.request({
|
|
432
|
+
method: "GET",
|
|
433
|
+
path: "/v1/usage/history",
|
|
434
|
+
query: {
|
|
435
|
+
days: params?.days
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// src/index.ts
|
|
442
|
+
var FluxVector = class {
|
|
443
|
+
collections;
|
|
444
|
+
vectors;
|
|
445
|
+
embeddings;
|
|
446
|
+
apiKeys;
|
|
447
|
+
usage;
|
|
448
|
+
searchResource;
|
|
449
|
+
constructor(config) {
|
|
450
|
+
if (!config.apiKey) {
|
|
451
|
+
throw new Error("FluxVector: apiKey is required");
|
|
452
|
+
}
|
|
453
|
+
const client = new HttpClient({
|
|
454
|
+
apiKey: config.apiKey,
|
|
455
|
+
baseUrl: config.baseUrl,
|
|
456
|
+
maxRetries: config.maxRetries,
|
|
457
|
+
timeout: config.timeout
|
|
458
|
+
});
|
|
459
|
+
this.collections = new Collections(client);
|
|
460
|
+
this.vectors = new Vectors(client);
|
|
461
|
+
this.searchResource = new Search(client);
|
|
462
|
+
this.embeddings = new Embeddings(client);
|
|
463
|
+
this.apiKeys = new ApiKeys(client);
|
|
464
|
+
this.usage = new Usage(client);
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Semantic search across a collection.
|
|
468
|
+
*
|
|
469
|
+
* @example
|
|
470
|
+
* const results = await fv.search('products', 'comfortable shoes', {
|
|
471
|
+
* topK: 5,
|
|
472
|
+
* filter: { price: { $lt: 100 } },
|
|
473
|
+
* });
|
|
474
|
+
*/
|
|
475
|
+
async search(collection, text, options) {
|
|
476
|
+
return this.searchResource.query(collection, text, options);
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
exports.ApiKeys = ApiKeys;
|
|
481
|
+
exports.AuthenticationError = AuthenticationError;
|
|
482
|
+
exports.Collections = Collections;
|
|
483
|
+
exports.Embeddings = Embeddings;
|
|
484
|
+
exports.FluxVector = FluxVector;
|
|
485
|
+
exports.FluxVectorError = FluxVectorError;
|
|
486
|
+
exports.HttpClient = HttpClient;
|
|
487
|
+
exports.NotFoundError = NotFoundError;
|
|
488
|
+
exports.RateLimitError = RateLimitError;
|
|
489
|
+
exports.Search = Search;
|
|
490
|
+
exports.ServerError = ServerError;
|
|
491
|
+
exports.Usage = Usage;
|
|
492
|
+
exports.ValidationError = ValidationError;
|
|
493
|
+
exports.Vectors = Vectors;
|
|
494
|
+
//# sourceMappingURL=index.cjs.map
|
|
495
|
+
//# sourceMappingURL=index.cjs.map
|