@premai/api-sdk 1.0.29
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 +899 -0
- package/dist/audio/index.d.ts +42 -0
- package/dist/cli.cjs +1684 -0
- package/dist/cli.d.ts +2 -0
- package/dist/config.d.ts +7 -0
- package/dist/core.browser.cjs +1404 -0
- package/dist/core.browser.mjs +1404 -0
- package/dist/core.d.ts +37 -0
- package/dist/files/index.d.ts +16 -0
- package/dist/index.cjs +1755 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +1691 -0
- package/dist/models/index.d.ts +5 -0
- package/dist/rvenc/index.d.ts +4 -0
- package/dist/server.d.ts +5 -0
- package/dist/tools/index.d.ts +23 -0
- package/dist/types.d.ts +364 -0
- package/dist/utils/attestation.d.ts +15 -0
- package/dist/utils/crypto.d.ts +20 -0
- package/dist/utils/dek-store.d.ts +7 -0
- package/dist/utils/error.d.ts +1 -0
- package/dist/utils/files.d.ts +2 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
# RVENC Client
|
|
2
|
+
|
|
3
|
+
End-to-end encrypted OpenAI-compatible client with file upload and tools support, using XWing (ML-KEM768 + X25519) hybrid post-quantum encryption.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import createRvencClient from "@premai/api-sdk-ts";
|
|
15
|
+
|
|
16
|
+
// Create client (encryption keys auto-generated)
|
|
17
|
+
const client = await createRvencClient({
|
|
18
|
+
apiKey: "your-api-key"
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Chat completion
|
|
22
|
+
const response = await client.chat.completions.create({
|
|
23
|
+
model: "openai/gpt-oss-120b",
|
|
24
|
+
messages: [{ role: "user", content: "Hello!" }],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
console.log(response.choices[0].message.content);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## OpenAI-Compatible API Server
|
|
31
|
+
|
|
32
|
+
Environment Variables:
|
|
33
|
+
|
|
34
|
+
* ENCLAVE_URL
|
|
35
|
+
* PROXY_URL
|
|
36
|
+
* CLIENT_KEK
|
|
37
|
+
* HOST (optional)
|
|
38
|
+
* PORT (optional)
|
|
39
|
+
|
|
40
|
+
Run as a standalone server with automatic DEK store management per API key:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# ways to run the termination proxy
|
|
44
|
+
## run command from local/remote NPM package
|
|
45
|
+
bunx -p @premai/api-sdk-ts pcci-proxy
|
|
46
|
+
npx -p @premai/api-sdk-ts pcci-proxy
|
|
47
|
+
|
|
48
|
+
## install globally
|
|
49
|
+
## make sure to have the "global bin dir" in your PATH
|
|
50
|
+
## `npm config get prefix`, for pure node runtime
|
|
51
|
+
npm i -g @premai/api-sdk-ts
|
|
52
|
+
bun i -g @premai/api-sdk-ts
|
|
53
|
+
|
|
54
|
+
# Server runs on http://localhost:3000
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Use with any OpenAI-compatible client:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
curl http://localhost:3000/v1/chat/completions \
|
|
61
|
+
-H "Authorization: Bearer your-api-key" \
|
|
62
|
+
-H "Content-Type: application/json" \
|
|
63
|
+
-d '{
|
|
64
|
+
"model": "openai/gpt-oss-120b",
|
|
65
|
+
"messages": [
|
|
66
|
+
{"role": "user", "content": "Hello!"}
|
|
67
|
+
],
|
|
68
|
+
"stream": false
|
|
69
|
+
}'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or use the OpenAI SDK in Node.js:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import OpenAI from "openai";
|
|
76
|
+
|
|
77
|
+
const client = new OpenAI({
|
|
78
|
+
apiKey: "your-api-key",
|
|
79
|
+
baseURL: "http://localhost:3000/v1",
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const stream = await client.chat.completions.create({
|
|
83
|
+
model: "openai/gpt-oss-120b",
|
|
84
|
+
messages: [{ role: "user", content: "Count to 10" }],
|
|
85
|
+
stream: true,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
for await (const chunk of stream) {
|
|
89
|
+
process.stdout.write(chunk.choices[0]?.delta?.content || "");
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The server caches clients in memory per API key for better performance.
|
|
94
|
+
|
|
95
|
+
## Features
|
|
96
|
+
|
|
97
|
+
- ✅ **Chat Completions** - OpenAI-compatible API with streaming support
|
|
98
|
+
- ✅ **Models** - List available models
|
|
99
|
+
- ✅ **File Management** - Upload, list, get, and delete encrypted files
|
|
100
|
+
- ✅ **File Upload** - Client-side encrypted file uploads with per-file DEKs
|
|
101
|
+
- ✅ **Tools** - Image generation, transcription, RAG search, and more
|
|
102
|
+
- ✅ **End-to-end Encryption** - Post-quantum cryptography (XWing)
|
|
103
|
+
- ✅ **KEK Architecture** - Key Encryption Key wraps all file DEKs
|
|
104
|
+
- ✅ **RAG Support** - Encrypted document retrieval with persistent RAG DEK
|
|
105
|
+
- ✅ **TypeScript** - Full type safety
|
|
106
|
+
|
|
107
|
+
## Chat Completions
|
|
108
|
+
|
|
109
|
+
### Non-streaming
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const response = await client.chat.completions.create({
|
|
113
|
+
model: "openai/gpt-oss-120b",
|
|
114
|
+
messages: [{ role: "user", content: "Hello!" }],
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
console.log(response.choices[0].message.content);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Streaming
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const stream = await client.chat.completions.create({
|
|
124
|
+
model: "openai/gpt-oss-120b",
|
|
125
|
+
messages: [{ role: "user", content: "Count to 10" }],
|
|
126
|
+
stream: true,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
for await (const chunk of stream) {
|
|
130
|
+
process.stdout.write(chunk.choices[0]?.delta?.content || "");
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Vision
|
|
135
|
+
|
|
136
|
+
Use vision models to analyze images:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const response = await client.chat.completions.create({
|
|
140
|
+
model: "OpenGVLab/InternVL3-38B",
|
|
141
|
+
messages: [{
|
|
142
|
+
role: "user",
|
|
143
|
+
content: [
|
|
144
|
+
{ type: "text", text: "What is in this image?" },
|
|
145
|
+
{
|
|
146
|
+
type: "image_url",
|
|
147
|
+
image_url: {
|
|
148
|
+
url: "https://fastly.picsum.photos/id/237/200/300.jpg?hmac=TmmQSbShHz9CdQm0NkEjx1Dyh_Y984R9LpNrpvH2D_U",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
}],
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
console.log(response.choices[0].message.content);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Audio
|
|
159
|
+
|
|
160
|
+
### Transcriptions
|
|
161
|
+
|
|
162
|
+
Transcribe audio files to text:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import createRvencClient from "@premai/api-sdk-ts";
|
|
166
|
+
import fs from "fs";
|
|
167
|
+
|
|
168
|
+
const client = await createRvencClient({
|
|
169
|
+
apiKey: "your-api-key"
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const transcription = await client.audio.transcriptions.create({
|
|
173
|
+
file: fs.createReadStream('./audio.wav'),
|
|
174
|
+
model: 'openai/whisper-large-v3',
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
console.log(transcription.text);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Translations
|
|
181
|
+
|
|
182
|
+
Translate audio files to English:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
const translation = await client.audio.translations.create({
|
|
186
|
+
file: fs.createReadStream('./audio.mp3'),
|
|
187
|
+
model: 'openai/whisper-large-v3',
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
console.log(translation.text);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
> **Note:** openai/whisper-large-v3 model supports non-streaming only.
|
|
194
|
+
|
|
195
|
+
## Models
|
|
196
|
+
|
|
197
|
+
List available models
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const client = await createRvencClient({
|
|
201
|
+
apiKey: "your-api-key",
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const models = await client.models.list({
|
|
205
|
+
type: "CHAT",
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## File Upload
|
|
210
|
+
|
|
211
|
+
### First Time Setup
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import createRvencClient, { serializeDEKStore } from "@premai/api-sdk-ts";
|
|
215
|
+
import fs from "fs";
|
|
216
|
+
|
|
217
|
+
// Create client (DEK store auto-generated)
|
|
218
|
+
const client = await createRvencClient({
|
|
219
|
+
apiKey: "your-api-key",
|
|
220
|
+
clientKEK: "your-kek" // You can pass clientKEK here or as an env variable CLIENT_KEK
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Save DEK store for future use
|
|
224
|
+
const serializedStore = serializeDEKStore(client.dekStore);
|
|
225
|
+
fs.writeFileSync("./dek-store.json", serializedStore);
|
|
226
|
+
|
|
227
|
+
console.log("✅ DEK store saved. Keep this file secure!");
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Subsequent Usage
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import createRvencClient, { deserializeDEKStore } from "@premai/api-sdk-ts";
|
|
234
|
+
import fs from "fs";
|
|
235
|
+
|
|
236
|
+
// Load existing DEK store
|
|
237
|
+
const serializedStore = fs.readFileSync("./dek-store.json", "utf-8");
|
|
238
|
+
const dekStore = deserializeDEKStore(serializedStore);
|
|
239
|
+
|
|
240
|
+
// Create client with existing DEK store
|
|
241
|
+
const client = await createRvencClient({
|
|
242
|
+
apiKey: "your-api-key",
|
|
243
|
+
clientKEK: "your-kek" // You can pass clientKEK here or as an env variable CLIENT_KEK
|
|
244
|
+
dekStore,
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Upload Files
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import fs from "fs";
|
|
252
|
+
import { serializeDEKStore } from "@premai/api-sdk-ts";
|
|
253
|
+
|
|
254
|
+
const fileContent = fs.readFileSync("./document.pdf");
|
|
255
|
+
const result = await client.files.upload({
|
|
256
|
+
file: new Uint8Array(fileContent),
|
|
257
|
+
fileName: "document.pdf",
|
|
258
|
+
mimeType: "application/pdf", // optional - auto-detected
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
console.log("Uploaded:", result.id);
|
|
262
|
+
|
|
263
|
+
// ⚠️ IMPORTANT: Save dekStore after upload to persist file DEKs
|
|
264
|
+
const serialized = serializeDEKStore(client.dekStore);
|
|
265
|
+
fs.writeFileSync("./dek-store.json", serialized);
|
|
266
|
+
console.log("✅ DEK store updated with file encryption keys");
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Upload Files with RAG Indexing
|
|
270
|
+
|
|
271
|
+
To enable RAG (Retrieval-Augmented Generation) indexing for a file, set `ragIndex: true`:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
const result = await client.files.upload({
|
|
275
|
+
file: new Uint8Array(fileContent),
|
|
276
|
+
fileName: "document.pdf",
|
|
277
|
+
mimeType: "application/pdf",
|
|
278
|
+
ragIndex: true, // Enable RAG indexing
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Save dekStore to persist both file DEK and RAG DEK
|
|
282
|
+
const serialized = serializeDEKStore(client.dekStore);
|
|
283
|
+
fs.writeFileSync("./dek-store.json", serialized);
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**RAG Indexing Notes:**
|
|
287
|
+
- When `ragIndex: true`, additional encryption keys are generated for RAG operations
|
|
288
|
+
- A shared `ragDEK` is created (or reused from dekStore) for all RAG-indexed files
|
|
289
|
+
- The file DEK is encrypted with the RAG DEK for retrieval operations
|
|
290
|
+
- RAG DEK is automatically persisted in `dekStore.ragDEK`
|
|
291
|
+
|
|
292
|
+
**Important Notes:**
|
|
293
|
+
- Each file is encrypted with a unique random DEK
|
|
294
|
+
- The DEK is wrapped with your `clientKEK` and stored in `dekStore.fileDEKs`
|
|
295
|
+
- **You must save the dekStore after uploading** to persist file DEKs
|
|
296
|
+
- File DEKs are required for tools to process uploaded files
|
|
297
|
+
|
|
298
|
+
### List Files
|
|
299
|
+
|
|
300
|
+
Retrieve a paginated list of your encrypted files with optional filtering:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// List all files (default: 20 per page)
|
|
304
|
+
const result = await client.files.list();
|
|
305
|
+
console.log(result.files); // Array of FileMetadata
|
|
306
|
+
console.log(result.pagination); // { total, page, limit, pages }
|
|
307
|
+
|
|
308
|
+
// List with pagination
|
|
309
|
+
const page2 = await client.files.list({
|
|
310
|
+
limit: 50,
|
|
311
|
+
offset: 50,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Filter by type
|
|
315
|
+
const images = await client.files.list({
|
|
316
|
+
type: 'image', // 'image' | 'document' | 'video' | 'audio' | 'archive' | 'general'
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Search by name
|
|
320
|
+
const searchResults = await client.files.list({
|
|
321
|
+
search: 'report',
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Filter by date range (must be ISO8601 format)
|
|
325
|
+
const recentFiles = await client.files.list({
|
|
326
|
+
from: '2024-01-01T00:00:00Z',
|
|
327
|
+
to: '2024-12-31T23:59:59Z',
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Or with timezone offset
|
|
331
|
+
const specificRange = await client.files.list({
|
|
332
|
+
from: '2024-01-01T00:00:00+00:00',
|
|
333
|
+
to: '2024-12-31T23:59:59+00:00',
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Combine filters
|
|
337
|
+
const filtered = await client.files.list({
|
|
338
|
+
limit: 100,
|
|
339
|
+
offset: 0,
|
|
340
|
+
type: 'document',
|
|
341
|
+
search: 'quarterly',
|
|
342
|
+
from: '2024-01-01',
|
|
343
|
+
to: '2024-12-31',
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Get File Details
|
|
348
|
+
|
|
349
|
+
Retrieve detailed information about a specific file:
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// Get file metadata
|
|
353
|
+
const file = await client.files.get({ id: 'file-id-123' });
|
|
354
|
+
console.log(file.original_name);
|
|
355
|
+
console.log(file.file_size);
|
|
356
|
+
console.log(file.mime_type);
|
|
357
|
+
console.log(file.created_at);
|
|
358
|
+
|
|
359
|
+
// Get file with signed download URL
|
|
360
|
+
const fileWithUrl = await client.files.get({
|
|
361
|
+
id: 'file-id-123',
|
|
362
|
+
url: true // Include signed download URL
|
|
363
|
+
});
|
|
364
|
+
console.log(fileWithUrl.url); // Temporary signed URL for download
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Delete File
|
|
368
|
+
|
|
369
|
+
Delete an encrypted file:
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
// Delete a file
|
|
373
|
+
await client.files.delete({
|
|
374
|
+
id: 'file-id-123'
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// With error handling
|
|
378
|
+
try {
|
|
379
|
+
await client.files.delete({
|
|
380
|
+
id: 'file-id-123'
|
|
381
|
+
});
|
|
382
|
+
console.log('File deleted successfully');
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error('Delete failed:', error.message);
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Index Files for RAG
|
|
389
|
+
|
|
390
|
+
Index existing files for RAG (Retrieval-Augmented Generation) search. This allows you to add files to your RAG index that were uploaded without `ragIndex: true`:
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
// First, list your files to get their IDs and paths
|
|
394
|
+
const filesList = await client.files.list();
|
|
395
|
+
|
|
396
|
+
// Select files to index
|
|
397
|
+
const filesToIndex = filesList.files
|
|
398
|
+
.filter(f => f.type === 'document')
|
|
399
|
+
.map(f => ({
|
|
400
|
+
fileId: f.id,
|
|
401
|
+
filePath: f.file_path,
|
|
402
|
+
}));
|
|
403
|
+
|
|
404
|
+
// Index the files for RAG (uses DEKs from dekStore)
|
|
405
|
+
const indexResult = await client.files.index({
|
|
406
|
+
files: filesToIndex
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// Check results
|
|
410
|
+
indexResult.data.results.forEach(result => {
|
|
411
|
+
if (result.success) {
|
|
412
|
+
console.log(`✓ File ${result.file_id}: ${result.rag_status}`);
|
|
413
|
+
} else {
|
|
414
|
+
console.error(`✗ File ${result.file_id} failed: ${result.error}`);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Flexible DEK Management:**
|
|
420
|
+
|
|
421
|
+
You can provide custom DEKs or let the function use DEKs from dekStore:
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
// Option 1: Use DEKs from dekStore (default)
|
|
425
|
+
await client.files.index({
|
|
426
|
+
files: [
|
|
427
|
+
{ fileId: 'file-id-1', filePath: 's3/path/file1.enc' },
|
|
428
|
+
{ fileId: 'file-id-2', filePath: 's3/path/file2.enc' },
|
|
429
|
+
]
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Option 2: Provide custom ragDEK
|
|
433
|
+
await client.files.index({
|
|
434
|
+
files: [
|
|
435
|
+
{ fileId: 'file-id-1', filePath: 's3/path/file1.enc' },
|
|
436
|
+
],
|
|
437
|
+
ragDEK: customRagDEK // Override dekStore ragDEK
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// Option 3: Provide custom fileDEK for specific files
|
|
441
|
+
await client.files.index({
|
|
442
|
+
files: [
|
|
443
|
+
{
|
|
444
|
+
fileId: 'file-id-1',
|
|
445
|
+
filePath: 's3/path/file1.enc',
|
|
446
|
+
fileDEK: customFileDEK // Custom DEK for this file
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
fileId: 'file-id-2',
|
|
450
|
+
filePath: 's3/path/file2.enc'
|
|
451
|
+
// Uses dekStore for this file
|
|
452
|
+
},
|
|
453
|
+
],
|
|
454
|
+
ragDEK: customRagDEK // Single ragDEK for all files
|
|
455
|
+
});
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**Important Notes:**
|
|
459
|
+
- **ragDEK**: Checks `options.ragDEK` first, then `dekStore.ragDEK`. Throws error if not found.
|
|
460
|
+
- **fileDEK**: For each file, checks `file.fileDEK` first, then `dekStore.fileDEKs`. Throws error if not found.
|
|
461
|
+
- To initialize RAG DEK in dekStore, upload at least one file with `ragIndex: true`
|
|
462
|
+
- Once indexed, files can be searched using `client.tools.searchRag()`
|
|
463
|
+
|
|
464
|
+
**Example Workflow:**
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
// Step 1: Upload first file with RAG to initialize RAG DEK
|
|
468
|
+
const firstFile = await client.files.upload({
|
|
469
|
+
file: new Uint8Array(fs.readFileSync('./doc1.pdf')),
|
|
470
|
+
fileName: 'doc1.pdf',
|
|
471
|
+
ragIndex: true, // Creates RAG DEK in dekStore
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// Save dekStore to persist RAG DEK
|
|
475
|
+
fs.writeFileSync('./dek-store.json', serializeDEKStore(client.dekStore));
|
|
476
|
+
|
|
477
|
+
// Step 2: Upload other files without RAG indexing (faster)
|
|
478
|
+
const file2 = await client.files.upload({
|
|
479
|
+
file: new Uint8Array(fs.readFileSync('./doc2.pdf')),
|
|
480
|
+
fileName: 'doc2.pdf',
|
|
481
|
+
ragIndex: false, // Skip RAG indexing during upload
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Step 3: Later, index the files for RAG
|
|
485
|
+
const result = await client.files.index({
|
|
486
|
+
files: [
|
|
487
|
+
{ fileId: file2.id, filePath: file2.file_path },
|
|
488
|
+
]
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
console.log('Indexed:', result.data.results[0].rag_status); // "running"
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Delete Files from RAG Index
|
|
495
|
+
|
|
496
|
+
Remove files from the RAG index without deleting the actual files:
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
// Delete specific files from RAG index
|
|
500
|
+
const deleteResult = await client.files.deleteIndex({
|
|
501
|
+
fileIds: ['file-id-1', 'file-id-2', 'file-id-3']
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Check results
|
|
505
|
+
deleteResult.data.results.forEach(result => {
|
|
506
|
+
if (result.success) {
|
|
507
|
+
console.log(`✓ File ${result.file_id} removed from index`);
|
|
508
|
+
} else {
|
|
509
|
+
console.error(`✗ File ${result.file_id} failed: ${result.error}`);
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
**With Custom RAG DEK:**
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
// Use custom ragDEK instead of dekStore
|
|
518
|
+
await client.files.deleteIndex({
|
|
519
|
+
fileIds: ['file-id-1', 'file-id-2'],
|
|
520
|
+
ragDEK: customRagDEK // Optional: override dekStore ragDEK
|
|
521
|
+
});
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
**Important Notes:**
|
|
525
|
+
- This removes files from the RAG search index only
|
|
526
|
+
- The actual encrypted files remain in storage
|
|
527
|
+
- **ragDEK**: Checks `options.ragDEK` first, then `dekStore.ragDEK`. Throws error if not found.
|
|
528
|
+
- Use `client.files.delete()` to completely delete files
|
|
529
|
+
|
|
530
|
+
### File Upload Options
|
|
531
|
+
|
|
532
|
+
```typescript
|
|
533
|
+
interface FileUploadOptions {
|
|
534
|
+
file: Uint8Array; // File content as bytes
|
|
535
|
+
fileName: string; // Name of the file
|
|
536
|
+
mimeType?: string; // Optional MIME type (auto-detected from extension)
|
|
537
|
+
ragIndex?: boolean; // Optional: Enable RAG indexing for this file
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
interface ListFilesOptions {
|
|
541
|
+
limit?: number; // Max files to return (default: 20)
|
|
542
|
+
offset?: number; // Skip N files for pagination (default: 0)
|
|
543
|
+
search?: string; // Search term to filter by name
|
|
544
|
+
from?: string; // Minimum date filter (ISO8601: YYYY-MM-DDTHH:mm:ss+HH:mm or Z)
|
|
545
|
+
to?: string; // Maximum date filter (ISO8601: YYYY-MM-DDTHH:mm:ss+HH:mm or Z)
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
interface GetFileOptions {
|
|
549
|
+
id: string; // File ID (required)
|
|
550
|
+
url?: boolean; // Include signed download URL (default: false)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
interface DeleteFileOptions {
|
|
554
|
+
id: string; // File ID (required)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
interface IndexFileInput {
|
|
558
|
+
fileId: string; // File ID (required)
|
|
559
|
+
filePath: string; // S3/R2 path to encrypted file (required)
|
|
560
|
+
fileDEK?: Uint8Array; // Optional: custom file DEK (falls back to dekStore)
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
interface IndexFilesOptions {
|
|
564
|
+
files: IndexFileInput[]; // Array of files to index (required)
|
|
565
|
+
ragDEK?: Uint8Array; // Optional: custom RAG DEK for all files (falls back to dekStore)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
interface DeleteIndexOptions {
|
|
569
|
+
fileIds: string[]; // Array of file IDs to remove from index (required)
|
|
570
|
+
ragDEK?: Uint8Array; // Optional: custom RAG DEK (falls back to dekStore)
|
|
571
|
+
}
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
## Tools
|
|
575
|
+
|
|
576
|
+
All tools are encrypted end-to-end. Files are automatically downloaded and decrypted.
|
|
577
|
+
|
|
578
|
+
### File-Producing Tools
|
|
579
|
+
|
|
580
|
+
Generate files that are automatically decrypted and returned:
|
|
581
|
+
|
|
582
|
+
```typescript
|
|
583
|
+
// Generate an image
|
|
584
|
+
const image = await client.tools.generateImage({prompt: "sunset over mountains"});
|
|
585
|
+
console.log(image.fileName); // "generated_image.png"
|
|
586
|
+
console.log(image.content); // Uint8Array - save or use directly
|
|
587
|
+
fs.writeFileSync(image.fileName, image.content);
|
|
588
|
+
|
|
589
|
+
// Generate audio from text
|
|
590
|
+
const audio = await client.tools.audioGenerateFromText({text: "Hello, world!"});
|
|
591
|
+
fs.writeFileSync(audio.fileName, audio.content);
|
|
592
|
+
|
|
593
|
+
// Create a custom file
|
|
594
|
+
const file = await client.tools.createFileForUser(
|
|
595
|
+
{
|
|
596
|
+
fileName: 'test_file',
|
|
597
|
+
fileExtension: 'txt',
|
|
598
|
+
fileContent: 'This is the content of the test file.',
|
|
599
|
+
mimeType: 'text/plain'
|
|
600
|
+
}
|
|
601
|
+
);
|
|
602
|
+
fs.writeFileSync(file.fileName, file.content);
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
### File-Processing Tools
|
|
606
|
+
|
|
607
|
+
Process uploaded files and get results:
|
|
608
|
+
|
|
609
|
+
```typescript
|
|
610
|
+
// Upload a file first
|
|
611
|
+
const upload = await client.files.upload({
|
|
612
|
+
file: new Uint8Array(fs.readFileSync("./image.jpg")),
|
|
613
|
+
fileName: "image.jpg",
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
// Describe and caption image
|
|
617
|
+
const description = await client.tools.imageDescribeAndCaption({fileId: upload.id});
|
|
618
|
+
console.log(description);
|
|
619
|
+
|
|
620
|
+
// Extract PDF content
|
|
621
|
+
const pdfUpload = await client.files.upload({
|
|
622
|
+
file: new Uint8Array(fs.readFileSync("./doc.pdf")),
|
|
623
|
+
fileName: "doc.pdf",
|
|
624
|
+
});
|
|
625
|
+
const pdfContent = await client.tools.getPDFContent({fileId: pdfUpload.id});
|
|
626
|
+
console.log(pdfContent);
|
|
627
|
+
|
|
628
|
+
// Transcribe audio
|
|
629
|
+
const audioUpload = await client.files.upload({
|
|
630
|
+
file: new Uint8Array(fs.readFileSync("./audio.mp3")),
|
|
631
|
+
fileName: "audio.mp3",
|
|
632
|
+
});
|
|
633
|
+
const transcript = await client.tools.transcribeAudioToText({fileId: audioUpload.id});
|
|
634
|
+
console.log(transcript);
|
|
635
|
+
|
|
636
|
+
// Video description
|
|
637
|
+
const videoUpload = await client.files.upload({
|
|
638
|
+
file: new Uint8Array(fs.readFileSync("./video.mp4")),
|
|
639
|
+
fileName: "video.mp4",
|
|
640
|
+
});
|
|
641
|
+
const videoDesc = await client.tools.videoDescribeAndCaption({fileId: videoUpload.id});
|
|
642
|
+
console.log(videoDesc);
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### Simple Tools
|
|
646
|
+
|
|
647
|
+
Tools that don't require file handling:
|
|
648
|
+
|
|
649
|
+
```typescript
|
|
650
|
+
// Get current time
|
|
651
|
+
const time = await client.tools.getTime({timezone: 'America/New_York'});
|
|
652
|
+
console.log(time);
|
|
653
|
+
|
|
654
|
+
// Web search
|
|
655
|
+
const searchResults = await client.tools.webSearchTool({query: 'latest AI news'});
|
|
656
|
+
console.log(searchResults);
|
|
657
|
+
|
|
658
|
+
// Web page scraper
|
|
659
|
+
const pageContent = await client.tools.webPageScraperTool({url: 'https://example.com', renderJs: false});
|
|
660
|
+
console.log(pageContent);
|
|
661
|
+
|
|
662
|
+
// RAG search across your uploaded files
|
|
663
|
+
const ragResults = await client.tools.searchRag({
|
|
664
|
+
query: 'test query',
|
|
665
|
+
});
|
|
666
|
+
console.log(ragResults);
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
## Available Tools
|
|
670
|
+
|
|
671
|
+
### File-Producing Tools
|
|
672
|
+
- `generateImage(params: { prompt: string })` - Generate images from text
|
|
673
|
+
- `audioGenerateFromText(params: { text: string })` - Text-to-speech
|
|
674
|
+
- `createFileForUser(params: { fileName: string, fileExtension: string, fileContent: string, mimeType: string })` - Create custom files
|
|
675
|
+
|
|
676
|
+
### File-Processing Tools
|
|
677
|
+
- `imageDescribeAndCaption(params: { fileId: string })` - Describe images
|
|
678
|
+
- `imageDescribeAndCaptionFallback(params: { fileId: string })` - Alternative image description
|
|
679
|
+
- `videoDescribeAndCaption(params: { fileId: string })` - Describe videos
|
|
680
|
+
- `getPDFContent(params: { fileId: string })` - Extract PDF text
|
|
681
|
+
- `getTextDocumentContent(params: { fileId: string })` - Extract document text
|
|
682
|
+
- `transcribeAudioToText(params: { fileId: string, language?: string })` - Audio transcription
|
|
683
|
+
- `transcribeAudioWithDiarization(params: { fileId: string, language?: string })` - Transcription with speakers
|
|
684
|
+
- `audioDiarization(params: { fileId: string })` - Identify speakers
|
|
685
|
+
- `getFileContentOCR(params: { fileId: string })` - OCR on images
|
|
686
|
+
- `getSpreadsheetContent(params: { fileId: string })` - Extract spreadsheet data
|
|
687
|
+
- `getDataFileContent(params: { fileId: string })` - Extract data file content
|
|
688
|
+
- `getPowerPointContent(params: { fileId: string, slideNumbers?: number[] })` - Extract presentation content
|
|
689
|
+
|
|
690
|
+
### Simple Tools
|
|
691
|
+
- `getTime(params: { timezone: string })` - Get current time
|
|
692
|
+
- `webSearchTool(params: { query: string, country?: string, searchLang?: string })` - Web search
|
|
693
|
+
- `webPageScraperTool(params: { url: string, renderJs?: boolean })` - Scrape web pages
|
|
694
|
+
|
|
695
|
+
### RAG Tools
|
|
696
|
+
- `searchRag(params: { query: string })` - Search indexed documents with optional file filtering
|
|
697
|
+
|
|
698
|
+
## Configuration Options
|
|
699
|
+
|
|
700
|
+
| Option | Type | Default | Description |
|
|
701
|
+
|--------|------|---------|-------------|
|
|
702
|
+
| `apiKey` | `string` | **required** | Authorization token |
|
|
703
|
+
| `encryptionKeys` | `EncryptionKeys` | auto-generated | Pre-generated ML-KEM keys |
|
|
704
|
+
| `dekStore` | `DEKStore` | auto-generated | DEKs for files and RAG |
|
|
705
|
+
| `requestTimeoutMs` | `number` | `30000` | Request timeout in milliseconds |
|
|
706
|
+
| `maxBufferSize` | `number` | `10485760` | Max SSE buffer size (10MB) |
|
|
707
|
+
|
|
708
|
+
## DEK Store Management
|
|
709
|
+
|
|
710
|
+
The DEK store contains encryption keys for all file and RAG operations:
|
|
711
|
+
|
|
712
|
+
```typescript
|
|
713
|
+
interface DEKStore {
|
|
714
|
+
fileDEKs?: Map<string, Uint8Array>; // Per-file DEKs (fileId -> DEK)
|
|
715
|
+
ragDEK?: Uint8Array; // RAG operations DEK
|
|
716
|
+
ragVersion?: string; // RAG version identifier
|
|
717
|
+
}
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
### Initialize New DEK Store
|
|
721
|
+
|
|
722
|
+
```typescript
|
|
723
|
+
import { initializeDEKStore, serializeDEKStore } from "@premai/api-sdk-ts";
|
|
724
|
+
|
|
725
|
+
// Create new DEK store with KEK from environment variable
|
|
726
|
+
const dekStore = initializeDEKStore();
|
|
727
|
+
|
|
728
|
+
// Save to secure storage
|
|
729
|
+
const serialized = serializeDEKStore(dekStore);
|
|
730
|
+
fs.writeFileSync("dek-store.json", serialized);
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
### Serialize for Storage
|
|
734
|
+
|
|
735
|
+
```typescript
|
|
736
|
+
import { serializeDEKStore } from "@premai/api-sdk-ts";
|
|
737
|
+
|
|
738
|
+
const serialized = serializeDEKStore(client.dekStore);
|
|
739
|
+
fs.writeFileSync("dek-store.json", serialized);
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
### Deserialize from Storage
|
|
743
|
+
|
|
744
|
+
```typescript
|
|
745
|
+
import { deserializeDEKStore } from "@premai/api-sdk-ts";
|
|
746
|
+
|
|
747
|
+
const serialized = fs.readFileSync("dek-store.json", "utf-8");
|
|
748
|
+
const dekStore = deserializeDEKStore(serialized);
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
### Important: Persist After File Operations
|
|
752
|
+
|
|
753
|
+
```typescript
|
|
754
|
+
// After uploading files
|
|
755
|
+
const upload = await client.files.upload({...});
|
|
756
|
+
|
|
757
|
+
// ⚠️ MUST save to persist file DEKs
|
|
758
|
+
const serialized = serializeDEKStore(client.dekStore);
|
|
759
|
+
fs.writeFileSync("dek-store.json", serialized);
|
|
760
|
+
|
|
761
|
+
// After using RAG tools
|
|
762
|
+
await client.tools.searchRag({query: "..."});
|
|
763
|
+
|
|
764
|
+
// ⚠️ Save if ragDEK was auto-generated
|
|
765
|
+
const serialized = serializeDEKStore(client.dekStore);
|
|
766
|
+
fs.writeFileSync("dek-store.json", serialized);
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
## How It Works
|
|
770
|
+
|
|
771
|
+
### Chat Completions
|
|
772
|
+
|
|
773
|
+
1. **Key Exchange**: Fetches enclave public key → creates XWing encapsulation → shared secret
|
|
774
|
+
2. **Request Encryption**: Encrypts inference params with XChaCha20-Poly1305
|
|
775
|
+
3. **Encrypted Transport**: Sends `{ cipherText, encryptedInference, nonce }` to proxy
|
|
776
|
+
4. **Response Decryption**: Decrypts response (streaming SSE or JSON) using shared secret
|
|
777
|
+
|
|
778
|
+
### File Upload
|
|
779
|
+
|
|
780
|
+
1. **DEK Generation**: Generates random 32-byte DEK for this file
|
|
781
|
+
2. **File Encryption**: Encrypts file content with XChaCha20-Poly1305 + managed nonce
|
|
782
|
+
3. **Metadata Encryption**: Encrypts filename and MIME type with same DEK
|
|
783
|
+
4. **DEK Wrapping**: Wraps DEK with `clientKEK` using AES-KWP
|
|
784
|
+
5. **Upload**: Sends encrypted data with `wrapped_dek` and `kid`
|
|
785
|
+
6. **Storage**: Stores file DEK in `dekStore.fileDEKs[fileId]` for later use
|
|
786
|
+
|
|
787
|
+
#### With RAG Indexing (`ragIndex: true`)
|
|
788
|
+
|
|
789
|
+
1. **RAG DEK**: Retrieves existing `ragDEK` from dekStore or generates new 32-byte key
|
|
790
|
+
2. **File DEK Encryption**: Encrypts file DEK with RAG DEK (XChaCha20-Poly1305)
|
|
791
|
+
3. **RAG DEK Encryption**: Encrypts RAG DEK with `clientKEK` (XChaCha20-Poly1305)
|
|
792
|
+
4. **Additional Payload**: Includes `encrypted_file_dek`, `encrypted_rag_dek`, `file_nonce`, `rag_dek_nonce`, `cipher_text`
|
|
793
|
+
5. **RAG Storage**: Persists `ragDEK` in `dekStore.ragDEK` for reuse across files
|
|
794
|
+
|
|
795
|
+
### Tools
|
|
796
|
+
|
|
797
|
+
#### File-Processing Tools
|
|
798
|
+
1. **Retrieve fileDEK**: Gets stored DEK from `dekStore.fileDEKs[fileId]`
|
|
799
|
+
2. **Encrypt fileDEK**: Encrypts fileDEK with shared secret (per-request nonce)
|
|
800
|
+
3. **Send Request**: Sends `encryptedFileDEK` + `fileDEKNonce` to enclave
|
|
801
|
+
4. **Enclave Decrypts**: Enclave decrypts fileDEK, then decrypts file from S3
|
|
802
|
+
5. **Response**: Returns encrypted result, SDK decrypts with shared secret
|
|
803
|
+
|
|
804
|
+
#### File-Producing Tools
|
|
805
|
+
1. **Generate DEK**: Creates random DEK for output file
|
|
806
|
+
2. **Encrypt Request**: Encrypts tool parameters with shared secret
|
|
807
|
+
3. **Enclave Executes**: Generates file, encrypts with provided DEK
|
|
808
|
+
4. **Download**: SDK downloads and decrypts file using the DEK
|
|
809
|
+
5. **Response**: Returns `DecryptedFile` with content as Uint8Array
|
|
810
|
+
|
|
811
|
+
#### RAG Tools
|
|
812
|
+
1. **Retrieve fileDEKs**: Gets DEKs for all specified files
|
|
813
|
+
2. **Encrypt DEKs**: Encrypts each fileDEK with shared secret (unique nonces)
|
|
814
|
+
3. **Encrypt ragDEK**: Encrypts persistent `ragDEK` with shared secret
|
|
815
|
+
4. **Send Request**: Sends `encryptedFileDEKs[]` + `ragDEK` + `ragVersion`
|
|
816
|
+
5. **Enclave Processing**: Decrypts files, performs RAG search
|
|
817
|
+
6. **Response**: Returns encrypted results
|
|
818
|
+
|
|
819
|
+
## Error Handling
|
|
820
|
+
|
|
821
|
+
```typescript
|
|
822
|
+
try {
|
|
823
|
+
const client = await createRvencClient({
|
|
824
|
+
apiKey: "your-api-key",
|
|
825
|
+
clientKEK: "your-kek" // You can pass clientKEK here or as an env variable CLIENT_KEK
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
const image = await client.tools.generateImage("mountain landscape");
|
|
829
|
+
fs.writeFileSync(image.fileName, image.content);
|
|
830
|
+
|
|
831
|
+
} catch (error) {
|
|
832
|
+
if (error.message.includes("timeout")) {
|
|
833
|
+
console.error("Request timed out");
|
|
834
|
+
} else {
|
|
835
|
+
console.error("Error:", error.message);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
## Security Notes
|
|
841
|
+
|
|
842
|
+
- ⚠️ **Use environment variables** for API keys & client KEK
|
|
843
|
+
- ⚠️ **Always persist dekStore after file uploads** - file DEKs must be saved
|
|
844
|
+
- ⚠️ **Backup your dekStore** - losing it means losing access to uploaded files
|
|
845
|
+
- ✅ All encryption happens client-side (zero-knowledge)
|
|
846
|
+
- ✅ Files are encrypted with unique DEKs before upload
|
|
847
|
+
- ✅ KEK-based architecture - KEK wraps all file DEKs
|
|
848
|
+
- ✅ Tool responses are automatically decrypted
|
|
849
|
+
- ✅ Post-quantum secure (XWing: ML-KEM768 + X25519)
|
|
850
|
+
|
|
851
|
+
### Encryption Architecture
|
|
852
|
+
|
|
853
|
+
```
|
|
854
|
+
clientKEK (32 bytes)
|
|
855
|
+
├── wraps fileDEK₁ → file_1.pdf
|
|
856
|
+
├── wraps fileDEK₂ → file_2.jpg
|
|
857
|
+
├── wraps fileDEK₃ → file_3.mp3
|
|
858
|
+
└── wraps fileDEKₙ → file_n.txt
|
|
859
|
+
|
|
860
|
+
ragDEK (32 bytes) → encrypts RAG index
|
|
861
|
+
|
|
862
|
+
Each file gets unique DEK, all wrapped by KEK
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
### Best Practices
|
|
866
|
+
|
|
867
|
+
1. **Initialize Once**: Create dekStore once, reuse across sessions
|
|
868
|
+
2. **Persist After Uploads**: Save dekStore immediately after each file upload
|
|
869
|
+
3. **Secure Storage**: Store `dek-store.json` in secure, encrypted location
|
|
870
|
+
4. **Regular Backups**: Backup dekStore to prevent data loss
|
|
871
|
+
5. **Rotation**: Generate new dekStore periodically and re-upload files
|
|
872
|
+
6. **Error Handling**: Always check if fileDEK exists before using tools
|
|
873
|
+
|
|
874
|
+
## TypeScript Types
|
|
875
|
+
|
|
876
|
+
```typescript
|
|
877
|
+
import type {
|
|
878
|
+
RvencClient,
|
|
879
|
+
RvencClientOptions,
|
|
880
|
+
DEKStore,
|
|
881
|
+
EncryptionKeys,
|
|
882
|
+
FileUploadOptions,
|
|
883
|
+
UploadedFile,
|
|
884
|
+
DecryptedFile,
|
|
885
|
+
ToolsClient,
|
|
886
|
+
ListFilesOptions,
|
|
887
|
+
ListFilesResponse,
|
|
888
|
+
GetFileOptions,
|
|
889
|
+
DeleteFileOptions,
|
|
890
|
+
DeleteFileResponse,
|
|
891
|
+
IndexFileInput,
|
|
892
|
+
IndexFilesOptions,
|
|
893
|
+
IndexFilesResponse,
|
|
894
|
+
DeleteIndexOptions,
|
|
895
|
+
DeleteIndexResponse,
|
|
896
|
+
FileMetadata,
|
|
897
|
+
PaginationInfo,
|
|
898
|
+
} from "@premai/api-sdk-ts";
|
|
899
|
+
```
|