ragora 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ragora
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,397 @@
1
+ # Ragora Node.js SDK
2
+
3
+ Official Node.js/TypeScript SDK for the [Ragora](https://ragora.app) RAG API. Build AI-powered knowledge bases with semantic search and chat completions.
4
+
5
+ [![npm version](https://badge.fury.io/js/ragora.svg)](https://www.npmjs.com/package/ragora)
6
+ [![Node.js 18+](https://img.shields.io/badge/node-18+-green.svg)](https://nodejs.org/)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install ragora
14
+ ```
15
+
16
+ Or with other package managers:
17
+
18
+ ```bash
19
+ pnpm add ragora
20
+ yarn add ragora
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { RagoraClient } from 'ragora';
27
+
28
+ const client = new RagoraClient({ apiKey: 'your-api-key' });
29
+
30
+ // Create a collection
31
+ const collection = await client.createCollection({
32
+ name: 'My Knowledge Base',
33
+ description: 'Documentation and guides',
34
+ });
35
+ console.log(`Created collection: ${collection.id}`);
36
+
37
+ // Upload a document
38
+ const upload = await client.uploadDocument({
39
+ file: new Blob(['Hello world']),
40
+ filename: 'hello.txt',
41
+ collectionId: collection.id,
42
+ });
43
+ console.log(`Uploaded: ${upload.filename} (ID: ${upload.id})`);
44
+
45
+ // Wait for processing to complete
46
+ const status = await client.waitForDocument(upload.id);
47
+ console.log(`Processing complete: ${status.vectorCount} vectors created`);
48
+
49
+ // Search the collection
50
+ const results = await client.search({
51
+ collectionId: collection.id,
52
+ query: 'How do I get started?',
53
+ topK: 5,
54
+ });
55
+ results.results.forEach((result) => {
56
+ console.log(`Score: ${result.score.toFixed(3)} - ${result.content.slice(0, 100)}...`);
57
+ });
58
+
59
+ // Chat with your knowledge base
60
+ const response = await client.chat({
61
+ collectionId: collection.id,
62
+ messages: [{ role: 'user', content: 'Summarize the main concepts' }],
63
+ });
64
+ console.log(response.choices[0].message.content);
65
+ ```
66
+
67
+ ## Features
68
+
69
+ - **Fetch-based** - Works in Node.js, browsers, and edge runtimes
70
+ - **Full TypeScript** - Complete type definitions included
71
+ - **Streaming support** - Real-time chat completions with async iterators
72
+ - **Document management** - Upload, track progress, and manage documents
73
+ - **Collection CRUD** - Create, update, delete, and list collections
74
+ - **Cost tracking** - Monitor API costs per request
75
+ - **Next.js ready** - Works with App Router and Pages Router
76
+
77
+ ## API Reference
78
+
79
+ ### Client Initialization
80
+
81
+ ```typescript
82
+ import { RagoraClient } from 'ragora';
83
+
84
+ // Basic usage
85
+ const client = new RagoraClient({ apiKey: 'your-api-key' });
86
+
87
+ // With custom settings
88
+ const client = new RagoraClient({
89
+ apiKey: 'your-api-key',
90
+ baseUrl: 'https://api.ragora.app', // default
91
+ timeout: 30000, // milliseconds
92
+ fetch: customFetch, // optional custom fetch implementation
93
+ });
94
+ ```
95
+
96
+ ### Collections
97
+
98
+ ```typescript
99
+ // Create a collection
100
+ const collection = await client.createCollection({
101
+ name: 'My Collection',
102
+ description: 'Optional description',
103
+ slug: 'my-collection', // optional, auto-generated if not provided
104
+ });
105
+
106
+ // List collections
107
+ const collections = await client.listCollections({ limit: 20, offset: 0 });
108
+ for (const coll of collections.data) {
109
+ console.log(`${coll.name}: ${coll.totalDocuments} documents`);
110
+ }
111
+
112
+ // Get a collection by ID or slug
113
+ const collection = await client.getCollection('collection-id-or-slug');
114
+
115
+ // Update a collection
116
+ const updated = await client.updateCollection('collection-id', {
117
+ name: 'New Name',
118
+ description: 'Updated description',
119
+ });
120
+
121
+ // Delete a collection
122
+ const result = await client.deleteCollection('collection-id');
123
+ console.log(result.message);
124
+ ```
125
+
126
+ ### Documents
127
+
128
+ ```typescript
129
+ // Upload a document (Node.js with Buffer)
130
+ import { readFileSync } from 'fs';
131
+
132
+ const upload = await client.uploadDocument({
133
+ file: readFileSync('./document.pdf'),
134
+ filename: 'document.pdf',
135
+ collectionId: 'collection-id', // optional, uses default if not provided
136
+ });
137
+
138
+ // Upload a document (Browser with Blob/File)
139
+ const upload = await client.uploadDocument({
140
+ file: fileInput.files[0], // from <input type="file">
141
+ filename: fileInput.files[0].name,
142
+ collectionId: 'collection-id',
143
+ });
144
+
145
+ // Check document status
146
+ const status = await client.getDocumentStatus(upload.id);
147
+ console.log(`Status: ${status.status}`);
148
+ console.log(`Progress: ${status.progressPercent}%`);
149
+ console.log(`Stage: ${status.progressStage}`);
150
+
151
+ // Wait for processing to complete
152
+ const status = await client.waitForDocument(upload.id, {
153
+ timeout: 300000, // max wait time in ms (default: 5 minutes)
154
+ pollInterval: 2000, // time between status checks in ms
155
+ });
156
+
157
+ // List documents in a collection
158
+ const documents = await client.listDocuments({
159
+ collectionId: 'collection-id',
160
+ limit: 50,
161
+ offset: 0,
162
+ });
163
+
164
+ // Delete a document
165
+ const result = await client.deleteDocument('document-id');
166
+ ```
167
+
168
+ ### Search
169
+
170
+ ```typescript
171
+ const results = await client.search({
172
+ collectionId: 'collection-id',
173
+ query: 'What is machine learning?',
174
+ topK: 5, // number of results
175
+ threshold: 0.7, // minimum relevance score (0-1)
176
+ filter: { type: 'doc' }, // optional metadata filter
177
+ });
178
+
179
+ for (const result of results.results) {
180
+ console.log(`Score: ${result.score.toFixed(3)}`);
181
+ console.log(`Content: ${result.content}`);
182
+ console.log(`Document ID: ${result.documentId}`);
183
+ console.log('---');
184
+ }
185
+ ```
186
+
187
+ ### Chat Completions
188
+
189
+ ```typescript
190
+ // Non-streaming
191
+ const response = await client.chat({
192
+ collectionId: 'collection-id',
193
+ messages: [
194
+ { role: 'system', content: 'You are a helpful assistant.' },
195
+ { role: 'user', content: 'Explain RAG' },
196
+ ],
197
+ model: 'gpt-4o-mini', // optional
198
+ temperature: 0.7, // optional
199
+ maxTokens: 1000, // optional
200
+ systemPrompt: 'Custom system prompt', // optional
201
+ });
202
+
203
+ console.log(response.choices[0].message.content);
204
+ console.log(`Sources used: ${response.sources.length}`);
205
+
206
+ // Streaming
207
+ for await (const chunk of client.chatStream({
208
+ collectionId: 'collection-id',
209
+ messages: [{ role: 'user', content: 'Explain RAG' }],
210
+ })) {
211
+ process.stdout.write(chunk.content);
212
+
213
+ // Sources are included in the final chunk
214
+ if (chunk.sources.length > 0) {
215
+ console.log(`\n\nSources: ${chunk.sources.length}`);
216
+ }
217
+ }
218
+ ```
219
+
220
+ ### Marketplace
221
+
222
+ ```typescript
223
+ // Browse marketplace products
224
+ const products = await client.listMarketplace({ limit: 10, search: 'AI' });
225
+ for (const product of products.data) {
226
+ console.log(`${product.title} - ${product.averageRating.toFixed(1)} stars`);
227
+ }
228
+
229
+ // Get product details (by ID or slug)
230
+ const product = await client.getMarketplaceProduct('product-slug');
231
+ console.log(`${product.title}: ${product.totalVectors} vectors`);
232
+ if (product.listings) {
233
+ for (const listing of product.listings) {
234
+ console.log(` ${listing.type}: $${listing.priceAmountUsd}`);
235
+ }
236
+ }
237
+ ```
238
+
239
+ ### Credits
240
+
241
+ ```typescript
242
+ const balance = await client.getBalance();
243
+ console.log(`Balance: $${balance.balanceUsd.toFixed(2)} ${balance.currency}`);
244
+ ```
245
+
246
+ ## Response Metadata
247
+
248
+ Every response includes metadata from API headers:
249
+
250
+ ```typescript
251
+ const response = await client.search({...});
252
+
253
+ console.log(`Request ID: ${response.requestId}`);
254
+ console.log(`API Version: ${response.apiVersion}`);
255
+ console.log(`Cost: $${response.costUsd?.toFixed(4)}`);
256
+ console.log(`Remaining balance: $${response.balanceRemainingUsd?.toFixed(2)}`);
257
+ console.log(`Rate limit: ${response.rateLimitRemaining}/${response.rateLimitLimit}`);
258
+ console.log(`Rate limit resets in: ${response.rateLimitReset}s`);
259
+ ```
260
+
261
+ ## Error Handling
262
+
263
+ ```typescript
264
+ import { RagoraClient, RagoraError } from 'ragora';
265
+
266
+ const client = new RagoraClient({ apiKey: 'your-api-key' });
267
+
268
+ try {
269
+ const results = await client.search({...});
270
+ } catch (error) {
271
+ if (error instanceof RagoraError) {
272
+ console.log(`Error: ${error.message}`);
273
+ console.log(`Status code: ${error.statusCode}`);
274
+ console.log(`Request ID: ${error.requestId}`);
275
+
276
+ if (error.isRateLimited) {
277
+ console.log('Rate limited - wait and retry');
278
+ } else if (error.isAuthError) {
279
+ console.log('Check your API key');
280
+ } else if (error.isRetryable) {
281
+ console.log('Temporary error - safe to retry');
282
+ }
283
+ }
284
+ }
285
+ ```
286
+
287
+ ## Next.js Integration
288
+
289
+ ### App Router (Server Components)
290
+
291
+ ```typescript
292
+ // app/api/search/route.ts
293
+ import { RagoraClient } from 'ragora';
294
+ import { NextResponse } from 'next/server';
295
+
296
+ const client = new RagoraClient({
297
+ apiKey: process.env.RAGORA_API_KEY!,
298
+ });
299
+
300
+ export async function POST(request: Request) {
301
+ const { query, collectionId } = await request.json();
302
+
303
+ const results = await client.search({
304
+ collectionId,
305
+ query,
306
+ topK: 5,
307
+ });
308
+
309
+ return NextResponse.json(results);
310
+ }
311
+ ```
312
+
313
+ ### Streaming Chat (App Router)
314
+
315
+ ```typescript
316
+ // app/api/chat/route.ts
317
+ import { RagoraClient } from 'ragora';
318
+
319
+ const client = new RagoraClient({
320
+ apiKey: process.env.RAGORA_API_KEY!,
321
+ });
322
+
323
+ export async function POST(request: Request) {
324
+ const { messages, collectionId } = await request.json();
325
+
326
+ const encoder = new TextEncoder();
327
+ const stream = new ReadableStream({
328
+ async start(controller) {
329
+ for await (const chunk of client.chatStream({
330
+ collectionId,
331
+ messages,
332
+ })) {
333
+ controller.enqueue(encoder.encode(chunk.content));
334
+ }
335
+ controller.close();
336
+ },
337
+ });
338
+
339
+ return new Response(stream, {
340
+ headers: { 'Content-Type': 'text/plain; charset=utf-8' },
341
+ });
342
+ }
343
+ ```
344
+
345
+ ### Pages Router
346
+
347
+ ```typescript
348
+ // pages/api/chat.ts
349
+ import { RagoraClient } from 'ragora';
350
+ import type { NextApiRequest, NextApiResponse } from 'next';
351
+
352
+ const client = new RagoraClient({
353
+ apiKey: process.env.RAGORA_API_KEY!,
354
+ });
355
+
356
+ export default async function handler(req: NextApiRequest, res: NextApiResponse) {
357
+ const { messages, collectionId } = req.body;
358
+
359
+ const response = await client.chat({
360
+ collectionId,
361
+ messages,
362
+ });
363
+
364
+ res.status(200).json(response);
365
+ }
366
+ ```
367
+
368
+ ## Examples
369
+
370
+ See the [`examples/`](examples/) directory for complete, runnable examples:
371
+
372
+ | Example | Description | Command |
373
+ |---------|-------------|---------|
374
+ | [Search](examples/search.ts) | Search documents and access response metadata | `npm run example:search` |
375
+ | [Chat](examples/chat.ts) | Chat completions with RAG context | `npm run example:chat` |
376
+ | [Streaming](examples/streaming.ts) | Streaming chat responses | `npm run example:streaming` |
377
+ | [Collections CRUD](examples/collections-crud.ts) | Create, list, get, update, delete collections | `npm run example:collections` |
378
+ | [Documents](examples/documents.ts) | Upload, process, list, delete documents | `npm run example:documents` |
379
+ | [Marketplace](examples/marketplace.ts) | Browse marketplace products and listings | `npm run example:marketplace` |
380
+ | [Credits](examples/credits.ts) | Check balance and track costs | `npm run example:credits` |
381
+
382
+ Set your API key before running:
383
+
384
+ ```bash
385
+ export RAGORA_API_KEY="your-api-key"
386
+ npm run example:search
387
+ ```
388
+
389
+ ## License
390
+
391
+ MIT License - see [LICENSE](LICENSE) for details.
392
+
393
+ ## Links
394
+
395
+ - [Ragora Website](https://ragora.app)
396
+ - [API Documentation](https://docs.ragora.app)
397
+ - [GitHub Repository](https://github.com/velarynai/ragora-node)
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Ragora API Client
3
+ *
4
+ * Fetch-based HTTP client for the Ragora API.
5
+ */
6
+ import type { ChatRequest, ChatResponse, ChatStreamChunk, Collection, CollectionListRequest, CollectionListResponse, CreateCollectionRequest, CreditBalance, DeleteResponse, DocumentListRequest, DocumentListResponse, DocumentStatus, MarketplaceListRequest, MarketplaceListResponse, MarketplaceProduct, SearchRequest, SearchResponse, UpdateCollectionRequest, UploadDocumentRequest, UploadResponse } from './types.js';
7
+ export interface RagoraClientOptions {
8
+ /** Your Ragora API key */
9
+ apiKey: string;
10
+ /** API base URL (default: https://api.ragora.app) */
11
+ baseUrl?: string;
12
+ /** Request timeout in milliseconds (default: 30000) */
13
+ timeout?: number;
14
+ /** Custom fetch implementation (for testing or environments without global fetch) */
15
+ fetch?: typeof fetch;
16
+ }
17
+ export declare class RagoraClient {
18
+ private readonly apiKey;
19
+ private readonly baseUrl;
20
+ private readonly timeout;
21
+ private readonly fetchFn;
22
+ constructor(options: RagoraClientOptions);
23
+ /**
24
+ * Extract metadata from response headers.
25
+ */
26
+ private extractMetadata;
27
+ /**
28
+ * Make an API request.
29
+ */
30
+ private request;
31
+ /**
32
+ * Handle error responses.
33
+ */
34
+ private handleError;
35
+ /**
36
+ * Search for relevant documents in a collection.
37
+ */
38
+ search(request: SearchRequest): Promise<SearchResponse>;
39
+ /**
40
+ * Generate a chat completion with RAG context.
41
+ */
42
+ chat(request: ChatRequest): Promise<ChatResponse>;
43
+ /**
44
+ * Stream a chat completion with RAG context.
45
+ */
46
+ chatStream(request: ChatRequest): AsyncGenerator<ChatStreamChunk>;
47
+ /**
48
+ * Get current credit balance.
49
+ */
50
+ getBalance(): Promise<CreditBalance>;
51
+ /**
52
+ * List your collections.
53
+ */
54
+ listCollections(request?: CollectionListRequest): Promise<CollectionListResponse>;
55
+ /**
56
+ * Get a specific collection by ID or slug.
57
+ */
58
+ getCollection(collectionId: string): Promise<Collection>;
59
+ /**
60
+ * Create a new collection.
61
+ */
62
+ createCollection(request: CreateCollectionRequest): Promise<Collection>;
63
+ /**
64
+ * Update an existing collection.
65
+ */
66
+ updateCollection(collectionId: string, request: UpdateCollectionRequest): Promise<Collection>;
67
+ /**
68
+ * Delete a collection and all its documents.
69
+ */
70
+ deleteCollection(collectionId: string): Promise<DeleteResponse>;
71
+ /**
72
+ * Upload a document to a collection.
73
+ */
74
+ uploadDocument(request: UploadDocumentRequest): Promise<UploadResponse>;
75
+ /**
76
+ * Get the processing status of a document.
77
+ */
78
+ getDocumentStatus(documentId: string): Promise<DocumentStatus>;
79
+ /**
80
+ * List documents in a collection.
81
+ */
82
+ listDocuments(request?: DocumentListRequest): Promise<DocumentListResponse>;
83
+ /**
84
+ * Delete a document.
85
+ */
86
+ deleteDocument(documentId: string): Promise<DeleteResponse>;
87
+ /**
88
+ * Wait for a document to finish processing.
89
+ */
90
+ waitForDocument(documentId: string, options?: {
91
+ /** Maximum time to wait in milliseconds (default: 300000 = 5 minutes) */
92
+ timeout?: number;
93
+ /** Time between status checks in milliseconds (default: 2000) */
94
+ pollInterval?: number;
95
+ }): Promise<DocumentStatus>;
96
+ /**
97
+ * List public marketplace products.
98
+ */
99
+ listMarketplace(request?: MarketplaceListRequest): Promise<MarketplaceListResponse>;
100
+ /**
101
+ * Get a marketplace product by ID or slug.
102
+ */
103
+ getMarketplaceProduct(idOrSlug: string): Promise<MarketplaceProduct>;
104
+ /**
105
+ * Map a raw marketplace product response to the SDK type.
106
+ */
107
+ private mapMarketplaceProduct;
108
+ }
109
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}