@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 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
+ ```