@hydradb/sdk 0.0.1 → 0.0.3

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 CHANGED
@@ -1,100 +1,147 @@
1
1
  # Hydra DB TypeScript SDK
2
2
 
3
- The official TypeScript SDK for the Hydra DB platform. Build powerful, context-aware AI applications in your Node.js or TypeScript projects.
3
+ The official TypeScript SDK for the Hydra DB platform.
4
4
 
5
- **Hydra DB** is your plug-and-play memory infrastructure. It powers intelligent, context-aware retrieval for any AI app or agent. Whether you're building a customer support bot, research copilot, or internal knowledge assistant - Hydra DB handles all!
5
+ Hydra DB provides memory, knowledge ingestion, retrieval, graph context, and raw-vector workflows for AI applications.
6
6
 
7
- [Learn more about the SDK from our docs](https://docs.hydradb.com/)
7
+ [Hydra DB docs](https://docs.hydradb.com/)
8
8
 
9
- ## Core features
9
+ ## Features
10
10
 
11
- * **Dynamic retrieval and querying** that always retrieves the most relevant context
12
- * **Built-in long-term memory** that evolves with every user interaction
13
- * **Personalization hooks** for user preferences, intent, and history
14
- * **Raw embeddings support** for bring-your-own vector workflows
15
- * **Developer-first SDK** with the most flexible APIs and fine-grained controls
11
+ - Upload files into a tenant knowledge base
12
+ - Add free-form memories, markdown, and user/assistant conversation memories
13
+ - Verify ingestion status after upload
14
+ - Search indexed knowledge and memories
15
+ - Fetch uploaded source content and graph relations
16
+ - Manage tenants and API keys
17
+ - Store, search, filter, and delete raw embeddings
18
+ - Use passthrough requests for endpoints not yet wrapped by the SDK
16
19
 
17
- ## Getting started
20
+ ## Installation
18
21
 
19
- ### Installation
20
-
21
- ```
22
- npm i @hydra_db/node
22
+ ```bash
23
+ npm install @hydradb/sdk
23
24
  # or
24
- yarn add @hydra_db/node
25
+ yarn add @hydradb/sdk
25
26
  # or
26
- pnpm add @hydra_db/node
27
+ pnpm add @hydradb/sdk
27
28
  ```
28
29
 
29
- ### Client setup
30
+ ## Client setup
30
31
 
31
32
  ```ts
32
- import { HydraDBClient } from "@hydra_db/node";
33
-
34
- process.loadEnvFile(".env");
33
+ import { HydraDBClient } from "@hydradb/sdk";
35
34
 
36
35
  const client = new HydraDBClient({
37
36
  token: process.env.HYDRA_DB_API_KEY,
38
37
  });
39
38
 
40
39
  const TENANT_ID = process.env.HYDRA_TENANT_ID ?? "my-company";
41
- const SUB_TENANT_ID = process.env.HYDRA_SUB_TENANT_ID ?? "";
40
+ const SUB_TENANT_ID = process.env.HYDRA_SUB_TENANT_ID ?? "my-sub-tenant";
42
41
  ```
43
42
 
44
- ---
43
+ The default API base URL is:
44
+
45
+ ```text
46
+ https://api.hydradb.com
47
+ ```
48
+
49
+ For local development, pass `baseUrl`:
50
+
51
+ ```ts
52
+ const localClient = new HydraDBClient({
53
+ token: process.env.HYDRA_DB_API_KEY,
54
+ baseUrl: "http://localhost:8080",
55
+ });
56
+ ```
57
+
58
+ ## Important tenant and sub-tenant rule
45
59
 
46
- ## Tenant Management
60
+ Use the same `tenant_id` and `sub_tenant_id` across upload, verify, recall, fetch, and delete calls.
47
61
 
48
- A `tenant` is a single isolated database. Within it you can create further isolated collections called `sub-tenants`. [Learn more](https://docs.hydradb.com/essentials/multi-tenant)
62
+ If you upload with a `sub_tenant_id` and then verify or search without it, you may check a different namespace. That can make statuses look inconsistent, for example `queued` in one namespace and `graph_creation` in another.
49
63
 
50
- ### Create a Tenant
64
+ ## Tenant management
65
+
66
+ A tenant is the top-level isolated database. A sub-tenant is an optional isolated collection inside a tenant.
67
+
68
+ ### Create a standard tenant
51
69
 
52
70
  ```ts
53
- const tenantResponse = await client.tenant.create({
71
+ await client.tenant.create({
54
72
  tenant_id: TENANT_ID,
55
73
  });
56
74
  ```
57
75
 
58
- You can also create a tenant optimised for raw vector embeddings:
76
+ ### Create an embeddings tenant
77
+
78
+ Raw embedding APIs require a tenant created with `is_embeddings_tenant: true` and a fixed embedding dimension.
59
79
 
60
80
  ```ts
61
- const embeddingsTenant = await client.tenant.create({
81
+ await client.tenant.create({
62
82
  tenant_id: "my-embeddings-tenant",
63
83
  is_embeddings_tenant: true,
64
84
  embeddings_dimension: 1536,
65
85
  });
66
86
  ```
67
87
 
68
- ### Get Sub-Tenant IDs
88
+ ### Create a tenant with metadata schema
89
+
90
+ ```ts
91
+ await client.tenant.create({
92
+ tenant_id: TENANT_ID,
93
+ tenant_metadata_schema: [
94
+ {
95
+ name: "department",
96
+ data_type: "VARCHAR",
97
+ enable_match: true,
98
+ enable_dense_embedding: false,
99
+ enable_sparse_embedding: false,
100
+ },
101
+ ],
102
+ });
103
+ ```
104
+
105
+ ### List sub-tenant IDs
69
106
 
70
107
  ```ts
71
108
  const subTenants = await client.tenant.getSubTenantIds({
72
109
  tenant_id: TENANT_ID,
73
110
  });
74
- // subTenants.sub_tenant_ids -> string[]
111
+
112
+ console.log(subTenants.sub_tenant_ids);
75
113
  ```
76
114
 
77
- ### Get Infrastructure Status
115
+ ### List tenant IDs
78
116
 
79
- Check whether the tenant's underlying infrastructure is ready:
117
+ ```ts
118
+ const tenants = await client.tenant.getTenantIds();
119
+ console.log(tenants.tenant_ids);
120
+ ```
121
+
122
+ ### Check tenant infrastructure status
80
123
 
81
124
  ```ts
82
125
  const infraStatus = await client.tenant.getInfraStatus({
83
126
  tenant_id: TENANT_ID,
84
127
  });
128
+
129
+ console.log(infraStatus);
85
130
  ```
86
131
 
87
- ### Monitor Tenant Stats
132
+ ### Monitor tenant stats
88
133
 
89
134
  ```ts
90
135
  const stats = await client.tenant.monitor({
91
136
  tenant_id: TENANT_ID,
92
137
  });
138
+
139
+ console.log(stats);
93
140
  ```
94
141
 
95
- ### Delete a Tenant
142
+ ### Delete a tenant
96
143
 
97
- > **Warning:** This is irreversible and permanently removes all data.
144
+ > Warning: this permanently deletes the tenant and its data.
98
145
 
99
146
  ```ts
100
147
  await client.tenant.deleteTenant({
@@ -102,42 +149,101 @@ await client.tenant.deleteTenant({
102
149
  });
103
150
  ```
104
151
 
105
- ---
152
+ ## Upload knowledge
106
153
 
107
- ## Index Your Data
154
+ Use `client.upload.knowledge()` to upload files to the knowledge base.
108
155
 
109
- ### Upload Knowledge (Files)
156
+ The SDK sends a multipart request to:
110
157
 
111
- Upload documents to make them retrievable via natural language search. Supports PDFs, text files, and other document formats.
158
+ ```text
159
+ POST /ingestion/upload_knowledge
160
+ ```
161
+
162
+ ### Upload one file
112
163
 
113
164
  ```ts
114
- import fs from "node:fs";
165
+ const uploadResult = await client.upload.knowledge({
166
+ tenant_id: TENANT_ID,
167
+ sub_tenant_id: SUB_TENANT_ID,
168
+ files: [
169
+ {
170
+ path: "./report.pdf",
171
+ filename: "report.pdf",
172
+ contentType: "application/pdf",
173
+ },
174
+ ],
175
+ upsert: true,
176
+ });
177
+
178
+ const sourceId = uploadResult.results?.[0]?.source_id;
179
+ console.log("source_id:", sourceId);
180
+ console.log("initial_status:", uploadResult.results?.[0]?.status);
181
+ ```
115
182
 
183
+ ### Upload multiple files
184
+
185
+ ```ts
116
186
  const uploadResult = await client.upload.knowledge({
117
187
  tenant_id: TENANT_ID,
118
188
  sub_tenant_id: SUB_TENANT_ID,
119
189
  files: [
120
- fs.readFileSync("report.pdf"),
121
- fs.readFileSync("notes.txt"),
190
+ {
191
+ path: "./a.pdf",
192
+ filename: "a.pdf",
193
+ contentType: "application/pdf",
194
+ },
195
+ {
196
+ path: "./notes.txt",
197
+ filename: "notes.txt",
198
+ contentType: "text/plain",
199
+ },
122
200
  ],
123
201
  upsert: true,
124
202
  });
125
- // uploadResult.results[0].source_id -> ID you can use later
203
+
204
+ console.log(uploadResult.results);
205
+ ```
206
+
207
+ You can also pass buffers, blobs, streams, or a file object with metadata:
208
+
209
+ ```ts
210
+ import { readFileSync } from "node:fs";
211
+
212
+ await client.upload.knowledge({
213
+ tenant_id: TENANT_ID,
214
+ sub_tenant_id: SUB_TENANT_ID,
215
+ files: [
216
+ {
217
+ data: readFileSync("./report.pdf"),
218
+ filename: "report.pdf",
219
+ contentType: "application/pdf",
220
+ },
221
+ ],
222
+ });
126
223
  ```
127
224
 
128
- You can attach metadata to each file by passing a JSON string. Each object in the array corresponds to the file at the same index:
225
+ ### Upload with metadata
226
+
227
+ `file_metadata` must be a JSON string. The array length should match the `files` array length.
228
+
229
+ Supported metadata object fields:
230
+
231
+ - `file_id`: optional custom source ID
232
+ - `metadata`: tenant-level metadata
233
+ - `additional_metadata`: document-level metadata
234
+ - `relations`: forceful relations to other Cortex/Hydra source IDs
129
235
 
130
236
  ```ts
131
237
  const fileMetadata = [
132
238
  {
133
- id: "doc_a",
134
- tenant_metadata: { dept: "sales" },
135
- document_metadata: { author: "Alice" },
239
+ file_id: "doc_a",
240
+ metadata: { department: "sales" },
241
+ additional_metadata: { author: "Alice" },
136
242
  },
137
243
  {
138
- id: "doc_b",
139
- tenant_metadata: { dept: "marketing" },
140
- document_metadata: { author: "Bob" },
244
+ file_id: "doc_b",
245
+ metadata: { department: "marketing" },
246
+ additional_metadata: { author: "Bob" },
141
247
  relations: {
142
248
  cortex_source_ids: ["doc_a"],
143
249
  properties: { relation: "same_upload_batch" },
@@ -145,21 +251,61 @@ const fileMetadata = [
145
251
  },
146
252
  ];
147
253
 
148
- const uploadWithMeta = await client.upload.knowledge({
254
+ const uploadResult = await client.upload.knowledge({
149
255
  tenant_id: TENANT_ID,
150
256
  sub_tenant_id: SUB_TENANT_ID,
151
257
  files: [
152
- fs.readFileSync("a.pdf"),
153
- fs.readFileSync("b.pdf"),
258
+ {
259
+ path: "./a.pdf",
260
+ filename: "a.pdf",
261
+ contentType: "application/pdf",
262
+ },
263
+ {
264
+ path: "./b.pdf",
265
+ filename: "b.pdf",
266
+ contentType: "application/pdf",
267
+ },
154
268
  ],
155
269
  file_metadata: JSON.stringify(fileMetadata),
156
270
  upsert: true,
157
271
  });
272
+
273
+ console.log(uploadResult.results);
158
274
  ```
159
275
 
160
- ### Verify Processing Status
276
+ Do not use old metadata keys such as `id`, `tenant_metadata`, or `document_metadata` inside `file_metadata` for file upload. The current upload endpoint expects `file_id`, `metadata`, and `additional_metadata`.
161
277
 
162
- After uploading, check when files have finished indexing:
278
+ ### Upload app-generated knowledge
279
+
280
+ You can index app-generated source objects without uploading files by passing `app_knowledge` as a JSON string.
281
+
282
+ ```ts
283
+ const appKnowledge = [
284
+ {
285
+ id: "app_source_1",
286
+ title: "CRM Account Note",
287
+ content: "Acme is interested in the enterprise plan.",
288
+ type: "crm_note",
289
+ tenant_metadata: { department: "sales" },
290
+ document_metadata: { source: "crm" },
291
+ },
292
+ ];
293
+
294
+ await client.upload.knowledge({
295
+ tenant_id: TENANT_ID,
296
+ sub_tenant_id: SUB_TENANT_ID,
297
+ app_knowledge: JSON.stringify(appKnowledge),
298
+ upsert: true,
299
+ });
300
+ ```
301
+
302
+ `app_sources` still exists in the generated SDK as a deprecated alias, but new code should use `app_knowledge`.
303
+
304
+ ## Verify ingestion status
305
+
306
+ Upload can return `queued` immediately. That means the file was accepted by the ingestion pipeline, not that indexing is complete.
307
+
308
+ Use `client.upload.verifyProcessing()` with the returned `source_id` values.
163
309
 
164
310
  ```ts
165
311
  const status = await client.upload.verifyProcessing({
@@ -167,14 +313,80 @@ const status = await client.upload.verifyProcessing({
167
313
  sub_tenant_id: SUB_TENANT_ID,
168
314
  file_ids: ["source-id-1", "source-id-2"],
169
315
  });
170
- // status.statuses[0].indexing_status -> "queued" | "processing" | "completed" | "errored"
316
+
317
+ console.log(status.statuses);
318
+ ```
319
+
320
+ Current processing status values:
321
+
322
+ ```text
323
+ queued
324
+ processing
325
+ graph_creation
326
+ completed
327
+ success
328
+ errored
329
+ ```
330
+
331
+ Meaning:
332
+
333
+ | Status | Meaning |
334
+ |---|---|
335
+ | `queued` | Upload accepted and waiting for processing |
336
+ | `processing` | Parsing, chunking, embedding, or indexing is running |
337
+ | `graph_creation` | Vector indexing is done, graph creation is still running |
338
+ | `completed` | Ingestion finished |
339
+ | `success` | Alias for completed |
340
+ | `errored` | Ingestion failed |
341
+
342
+ ### Poll until ingestion finishes
343
+
344
+ ```ts
345
+ const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
346
+
347
+ async function waitForIngestion(fileIds: string[]) {
348
+ while (true) {
349
+ const status = await client.upload.verifyProcessing({
350
+ tenant_id: TENANT_ID,
351
+ sub_tenant_id: SUB_TENANT_ID,
352
+ file_ids: fileIds,
353
+ });
354
+
355
+ const statuses = status.statuses;
356
+ console.log(statuses);
357
+
358
+ const failed = statuses.find((item) => item.indexing_status === "errored");
359
+ if (failed) {
360
+ throw new Error(failed.error_message ?? `Ingestion failed for ${failed.file_id}`);
361
+ }
362
+
363
+ const done = statuses.every((item) =>
364
+ item.indexing_status === "completed" || item.indexing_status === "success"
365
+ );
366
+
367
+ if (done) {
368
+ return statuses;
369
+ }
370
+
371
+ await sleep(5000);
372
+ }
373
+ }
374
+
375
+ const fileIds = uploadResult.results?.map((item) => item.source_id).filter(Boolean) ?? [];
376
+ await waitForIngestion(fileIds);
171
377
  ```
172
378
 
173
- ### Add Memories
379
+ ## Add memories
174
380
 
175
- Index free-form text, markdown content, or conversation pairs as searchable memories.
381
+ Use `client.upload.addMemory()` to add text, markdown, or conversation memories.
176
382
 
177
- **Plain text:**
383
+ The SDK sends JSON to:
384
+
385
+ ```text
386
+ POST /memories/add_memory
387
+ ```
388
+
389
+ ### Add plain text memory
178
390
 
179
391
  ```ts
180
392
  await client.upload.addMemory({
@@ -183,15 +395,19 @@ await client.upload.addMemory({
183
395
  upsert: true,
184
396
  memories: [
185
397
  {
186
- text: "User prefers detailed explanations and dark mode",
398
+ source_id: "memory_001",
399
+ text: "User prefers detailed technical explanations.",
400
+ title: "User preference",
187
401
  infer: true,
188
402
  user_name: "John",
403
+ metadata: { category: "preference" },
404
+ additional_metadata: { source: "chat" },
189
405
  },
190
406
  ],
191
407
  });
192
408
  ```
193
409
 
194
- **Markdown:**
410
+ ### Add markdown memory
195
411
 
196
412
  ```ts
197
413
  await client.upload.addMemory({
@@ -200,16 +416,17 @@ await client.upload.addMemory({
200
416
  upsert: true,
201
417
  memories: [
202
418
  {
203
- text: "# Meeting Notes\n\n## Key Points\n- Budget approved\n- Launch date: Q2",
419
+ source_id: "meeting_notes_001",
420
+ title: "Meeting Notes",
421
+ text: "# Meeting Notes\n\n- Budget approved\n- Launch planned for Q2",
204
422
  is_markdown: true,
205
423
  infer: false,
206
- title: "Meeting Notes",
207
424
  },
208
425
  ],
209
426
  });
210
427
  ```
211
428
 
212
- **User–assistant conversation pairs:**
429
+ ### Add user/assistant conversation memory
213
430
 
214
431
  ```ts
215
432
  await client.upload.addMemory({
@@ -218,119 +435,107 @@ await client.upload.addMemory({
218
435
  upsert: true,
219
436
  memories: [
220
437
  {
438
+ source_id: "conversation_001",
221
439
  user_assistant_pairs: [
222
- { user: "What are my preferences?", assistant: "You prefer dark mode and detailed explanations." },
223
- { user: "How do I like my reports?", assistant: "You prefer weekly summary reports with charts." },
440
+ {
441
+ user: "How do I like reports?",
442
+ assistant: "You prefer weekly summary reports with charts.",
443
+ },
224
444
  ],
225
445
  infer: true,
226
446
  user_name: "John",
227
- custom_instructions: "Extract user preferences",
447
+ custom_instructions: "Extract durable user preferences.",
228
448
  },
229
449
  ],
230
450
  });
231
451
  ```
232
452
 
233
- ### Delete a Memory
453
+ ### Delete a memory
234
454
 
235
455
  ```ts
236
456
  await client.upload.deleteMemory({
237
457
  tenant_id: TENANT_ID,
238
458
  sub_tenant_id: SUB_TENANT_ID,
239
- memory_id: "memory-source-id",
459
+ memory_id: "memory_001",
240
460
  });
241
461
  ```
242
462
 
243
- ---
244
-
245
- ## Search & Retrieval
463
+ ## Search and retrieval
246
464
 
247
- ### Full Recall
465
+ ### Full recall
248
466
 
249
- Hybrid semantic + keyword search across both knowledge and memories:
467
+ Hybrid semantic and keyword retrieval across indexed content.
250
468
 
251
469
  ```ts
252
470
  const results = await client.recall.fullRecall({
253
471
  tenant_id: TENANT_ID,
254
472
  sub_tenant_id: SUB_TENANT_ID,
255
- query: "Which mode does the user prefer?",
256
- alpha: 0.8, // 1.0 = pure semantic, 0.0 = pure keyword
257
- recency_bias: 0, // 0.0 = no bias, 1.0 = strongly prefer recent
473
+ query: "What did the account notes say about Acme?",
258
474
  max_results: 10,
475
+ mode: "fast",
476
+ alpha: 0.8,
477
+ recency_bias: 0,
478
+ graph_context: true,
259
479
  });
260
- // results.chunks -> VectorStoreChunk[]
261
- // results.sources -> SourceInfo[]
480
+
481
+ console.log(results.chunks);
482
+ console.log(results.sources);
262
483
  ```
263
484
 
264
- ### Recall Preferences
485
+ `alpha` can be a number from `0.0` to `1.0`, or the string `"auto"`.
486
+
487
+ ### Recall preferences
265
488
 
266
- Search only user memory/preference data:
489
+ Search user memory and preference data.
267
490
 
268
491
  ```ts
269
492
  const preferences = await client.recall.recallPreferences({
270
493
  tenant_id: TENANT_ID,
271
494
  sub_tenant_id: SUB_TENANT_ID,
272
- query: "dark mode preference",
495
+ query: "report format preference",
273
496
  max_results: 5,
274
497
  });
498
+
499
+ console.log(preferences.chunks);
275
500
  ```
276
501
 
277
- ### Boolean Recall
502
+ ### Boolean recall
278
503
 
279
- Exact keyword / phrase / boolean search (BM25):
504
+ Keyword, phrase, and BM25-style search.
280
505
 
281
506
  ```ts
282
507
  const boolResults = await client.recall.booleanRecall({
283
508
  tenant_id: TENANT_ID,
284
509
  sub_tenant_id: SUB_TENANT_ID,
285
- query: "dark mode",
286
- operator: "phrase", // "or" | "and" | "phrase"
510
+ query: "enterprise plan",
511
+ operator: "phrase",
512
+ search_mode: "sources",
287
513
  max_results: 10,
288
- search_mode: "memories", // "sources" | "memories"
289
514
  });
290
- ```
291
-
292
- ### Q&A (LLM-powered answer)
293
-
294
- Ask a question and get a grounded answer generated by an LLM over your indexed content:
295
515
 
296
- ```ts
297
- const answer = await client.recall.qna({
298
- tenant_id: TENANT_ID,
299
- sub_tenant_id: SUB_TENANT_ID,
300
- question: "What is the user's preferred reporting format?",
301
- mode: "fast", // "fast" | "thinking"
302
- search_mode: "memories",
303
- max_chunks: 6,
304
- });
516
+ console.log(boolResults.chunks);
305
517
  ```
306
518
 
307
- You can optionally choose the LLM provider and model:
519
+ Supported `operator` values:
308
520
 
309
- ```ts
310
- const customAnswer = await client.recall.qna({
311
- tenant_id: TENANT_ID,
312
- sub_tenant_id: SUB_TENANT_ID,
313
- question: "Summarise the budget decisions from the meeting notes.",
314
- mode: "thinking",
315
- search_mode: "sources",
316
- max_chunks: 10,
317
- llm_provider: "anthropic",
318
- model: "claude-sonnet-4-6",
319
- temperature: 0.2,
320
- max_tokens: 1024,
321
- });
521
+ ```text
522
+ or
523
+ and
524
+ phrase
322
525
  ```
323
526
 
324
- ---
527
+ Supported `search_mode` values:
325
528
 
326
- ## Fetch & Inspect Data
529
+ ```text
530
+ sources
531
+ memories
532
+ ```
327
533
 
328
- ### List All Data
534
+ ## Fetch and inspect data
329
535
 
330
- List sources (knowledge) or memories with optional filtering and pagination:
536
+ ### List knowledge sources
331
537
 
332
538
  ```ts
333
- // List knowledge sources
334
539
  const sources = await client.fetch.listData({
335
540
  tenant_id: TENANT_ID,
336
541
  sub_tenant_id: SUB_TENANT_ID,
@@ -339,15 +544,25 @@ const sources = await client.fetch.listData({
339
544
  page_size: 50,
340
545
  });
341
546
 
342
- // List user memories
547
+ console.log(sources.data);
548
+ console.log(sources.pagination);
549
+ ```
550
+
551
+ ### List memories
552
+
553
+ ```ts
343
554
  const memories = await client.fetch.listData({
344
555
  tenant_id: TENANT_ID,
345
556
  sub_tenant_id: SUB_TENANT_ID,
346
557
  kind: "memories",
558
+ page: 1,
559
+ page_size: 50,
347
560
  });
561
+
562
+ console.log(memories.data);
348
563
  ```
349
564
 
350
- Filter by metadata:
565
+ ### Filter listed data
351
566
 
352
567
  ```ts
353
568
  const filtered = await client.fetch.listData({
@@ -355,43 +570,70 @@ const filtered = await client.fetch.listData({
355
570
  sub_tenant_id: SUB_TENANT_ID,
356
571
  kind: "knowledge",
357
572
  filters: {
358
- tenant_metadata: { dept: "sales" },
573
+ tenant_metadata: { department: "sales" },
574
+ document_metadata: { author: "Alice" },
575
+ source_fields: { type: "crm_note" },
359
576
  },
360
577
  });
578
+
579
+ console.log(filtered.data);
361
580
  ```
362
581
 
363
- ### Fetch Source Content
582
+ ### Reduce list payload size
583
+
584
+ For `kind: "knowledge"`, you can use `include_fields` to return only selected source fields.
585
+
586
+ ```ts
587
+ const slimSources = await client.fetch.listData({
588
+ tenant_id: TENANT_ID,
589
+ sub_tenant_id: SUB_TENANT_ID,
590
+ kind: "knowledge",
591
+ include_fields: ["title", "document_metadata", "timestamp"],
592
+ });
593
+
594
+ console.log(slimSources.data);
595
+ ```
364
596
 
365
- Retrieve the full content of a specific source by its ID:
597
+ ### Fetch source content
366
598
 
367
599
  ```ts
368
600
  const source = await client.fetch.content({
369
601
  tenant_id: TENANT_ID,
370
602
  sub_tenant_id: SUB_TENANT_ID,
371
- source_id: "your-source-id",
372
- mode: "content", // "content" | "url" | "both"
603
+ source_id: "source-id-1",
604
+ mode: "content",
373
605
  });
606
+
607
+ console.log(source);
374
608
  ```
375
609
 
376
- ### Fetch Graph Relations
610
+ Supported `mode` values:
611
+
612
+ ```text
613
+ content
614
+ url
615
+ both
616
+ ```
377
617
 
378
- Retrieve the graph relations (linked sources) for a given source:
618
+ ### Fetch graph relations
379
619
 
380
620
  ```ts
381
621
  const relations = await client.fetch.graphRelationsBySourceId({
382
622
  tenant_id: TENANT_ID,
383
623
  sub_tenant_id: SUB_TENANT_ID,
384
- source_id: "your-source-id",
624
+ source_id: "source-id-1",
385
625
  is_memory: false,
386
626
  limit: 10,
387
627
  });
628
+
629
+ console.log(relations);
388
630
  ```
389
631
 
390
- ---
632
+ If you omit `source_id`, the endpoint can return relations across the sub-tenant.
391
633
 
392
- ## Delete Data
634
+ ## Delete data
393
635
 
394
- Delete one or more sources (knowledge or memories) by their IDs:
636
+ Use `client.data.delete()` to delete one or more source IDs.
395
637
 
396
638
  ```ts
397
639
  await client.data.delete({
@@ -401,168 +643,189 @@ await client.data.delete({
401
643
  });
402
644
  ```
403
645
 
404
- ---
646
+ ## Graph health
405
647
 
406
- ## API Key Management
407
-
408
- > **Note:** This endpoint requires a dashboard session token (obtained via your Hydra DB dashboard login), not a standard API key. Use it from a server-side admin context where you have a valid user session.
409
-
410
- Create scoped API keys for controlled access:
648
+ Fetch high-degree graph nodes for a tenant or sub-tenant.
411
649
 
412
650
  ```ts
413
- const newKey = await client.key.createApiKey({
414
- owner: "service-account@myapp.com",
415
- scopes: ["query"],
416
- env: "live",
417
- prefix: "sk",
651
+ const superNodes = await client.graphHealth.getSuperNodes({
652
+ tenant_id: TENANT_ID,
653
+ sub_tenant_id: SUB_TENANT_ID,
654
+ degree_threshold: 50,
655
+ limit: 20,
418
656
  });
419
- // newKey.full_api_key -> the actual key (only shown once)
420
- ```
421
-
422
- ---
423
657
 
424
- ## Raw Embeddings
658
+ console.log(superNodes);
659
+ ```
425
660
 
426
- Use Hydra DB as a vector store with your own embeddings — useful when you want to manage embedding generation yourself.
661
+ ## Raw embeddings
427
662
 
428
- > **Note:** Raw embeddings require a tenant created with `is_embeddings_tenant: true` and a fixed `embeddings_dimension`. A standard knowledge tenant does not support raw embedding operations.
429
- >
430
- > ```ts
431
- > await client.tenant.create({
432
- > tenant_id: "my-embeddings-tenant",
433
- > is_embeddings_tenant: true,
434
- > embeddings_dimension: 1536,
435
- > });
436
- > ```
663
+ Raw embedding APIs are for bring-your-own-vector workflows. They require an embeddings tenant.
437
664
 
438
- ### Insert Embeddings
665
+ ### Insert raw embeddings
439
666
 
440
667
  ```ts
441
668
  await client.embeddings.insert({
442
- tenant_id: TENANT_ID,
669
+ tenant_id: "my-embeddings-tenant",
443
670
  sub_tenant_id: SUB_TENANT_ID,
444
671
  upsert: true,
445
672
  embeddings: [
446
673
  {
447
- source_id: "my-doc-001",
448
- metadata: { category: "finance", year: 2024 },
674
+ source_id: "doc_001",
675
+ metadata: { category: "finance", year: 2026 },
449
676
  embeddings: [
450
- { chunk_id: "my-doc-001-chunk-0", embedding: [0.1, 0.2, 0.3 /* ... 1536 dims */] },
451
- { chunk_id: "my-doc-001-chunk-1", embedding: [0.4, 0.5, 0.6 /* ... 1536 dims */] },
677
+ {
678
+ chunk_id: "doc_001_chunk_0",
679
+ embedding: [0.1, 0.2, 0.3],
680
+ },
452
681
  ],
453
682
  },
454
683
  ],
455
684
  });
456
685
  ```
457
686
 
458
- ### Search by Vector
687
+ The vector length must match the `embeddings_dimension` used when creating the embeddings tenant.
459
688
 
460
- Find the most similar chunks to a query embedding:
689
+ ### Search raw embeddings
461
690
 
462
691
  ```ts
463
- const searchResults = await client.embeddings.search({
464
- tenant_id: TENANT_ID,
692
+ const rawResults = await client.embeddings.search({
693
+ tenant_id: "my-embeddings-tenant",
465
694
  sub_tenant_id: SUB_TENANT_ID,
466
- query_embedding: [0.1, 0.2, 0.3 /* ... 1536 dims */],
695
+ query_embedding: [0.1, 0.2, 0.3],
467
696
  limit: 10,
468
697
  });
469
- // searchResults -> RawEmbeddingSearchResult[]
470
- ```
471
698
 
472
- ### Filter Embeddings
699
+ console.log(rawResults);
700
+ ```
473
701
 
474
- Retrieve stored embeddings for a specific source or set of chunk IDs:
702
+ ### Filter raw embeddings
475
703
 
476
704
  ```ts
477
- // By source
478
705
  const bySource = await client.embeddings.filter({
479
- tenant_id: TENANT_ID,
706
+ tenant_id: "my-embeddings-tenant",
480
707
  sub_tenant_id: SUB_TENANT_ID,
481
- source_id: "my-doc-001",
708
+ source_id: "doc_001",
482
709
  limit: 50,
483
710
  });
484
711
 
485
- // By chunk IDs
712
+ console.log(bySource);
713
+ ```
714
+
715
+ ```ts
486
716
  const byChunks = await client.embeddings.filter({
487
- tenant_id: TENANT_ID,
717
+ tenant_id: "my-embeddings-tenant",
488
718
  sub_tenant_id: SUB_TENANT_ID,
489
- chunk_ids: ["my-doc-001-chunk-0", "my-doc-001-chunk-1"],
719
+ chunk_ids: ["doc_001_chunk_0"],
490
720
  });
721
+
722
+ console.log(byChunks);
491
723
  ```
492
724
 
493
- ### Delete Embeddings
725
+ ### Delete raw embeddings
494
726
 
495
727
  ```ts
496
- // Delete all embeddings for a source
497
728
  await client.embeddings.delete({
498
- tenant_id: TENANT_ID,
729
+ tenant_id: "my-embeddings-tenant",
499
730
  sub_tenant_id: SUB_TENANT_ID,
500
- source_id: "my-doc-001",
731
+ source_id: "doc_001",
501
732
  });
733
+ ```
502
734
 
503
- // Delete specific chunks
735
+ ```ts
504
736
  await client.embeddings.delete({
505
- tenant_id: TENANT_ID,
737
+ tenant_id: "my-embeddings-tenant",
506
738
  sub_tenant_id: SUB_TENANT_ID,
507
- chunk_ids: ["my-doc-001-chunk-0"],
739
+ chunk_ids: ["doc_001_chunk_0"],
508
740
  });
509
741
  ```
510
742
 
511
- ---
512
-
513
- ## Platform Metrics
743
+ ## API key management
514
744
 
515
- Retrieve platform-level health and usage metrics:
745
+ > This endpoint is meant for an authenticated dashboard/admin context. Do not expose admin credentials in client-side applications.
516
746
 
517
747
  ```ts
518
- const metrics = await client.metricsMetricsGet();
748
+ const apiKey = await client.key.createApiKey({
749
+ owner: "service-account@example.com",
750
+ scopes: ["ingest", "query"],
751
+ env: "live",
752
+ prefix: "sk",
753
+ });
754
+
755
+ console.log(apiKey.full_api_key);
519
756
  ```
520
757
 
521
- ---
758
+ ## Platform metrics
522
759
 
523
- ## SDK Method Reference
760
+ ```ts
761
+ const metrics = await client.metricsMetricsGet();
762
+ console.log(metrics);
763
+ ```
524
764
 
525
- | Method | Description |
526
- |---|---|
527
- | `client.tenant.create` | Create a new tenant (standard or embeddings) |
528
- | `client.tenant.getSubTenantIds` | List all sub-tenant IDs within a tenant |
529
- | `client.tenant.getInfraStatus` | Check tenant infrastructure readiness |
530
- | `client.tenant.monitor` | Get tenant usage and stats |
531
- | `client.tenant.deleteTenant` | Permanently delete a tenant and all its data |
532
- | `client.upload.knowledge` | Upload files to the knowledge base |
533
- | `client.upload.verifyProcessing` | Poll indexing status of uploaded files |
534
- | `client.upload.addMemory` | Index text, markdown, or conversation pairs as memories |
535
- | `client.upload.deleteMemory` | Delete a specific memory by ID |
536
- | `client.recall.fullRecall` | Hybrid semantic + keyword search |
537
- | `client.recall.recallPreferences` | Search user memory / preference data only |
538
- | `client.recall.booleanRecall` | Exact keyword / phrase / boolean search |
539
- | `client.recall.qna` | LLM-powered question answering over indexed content |
540
- | `client.fetch.listData` | List all knowledge sources or memories |
541
- | `client.fetch.content` | Fetch full content of a source by ID |
542
- | `client.fetch.graphRelationsBySourceId` | Fetch graph relations for a source |
543
- | `client.data.delete` | Delete sources or memories by ID |
544
- | `client.key.createApiKey` | Create a scoped API key *(requires dashboard session token)* |
545
- | `client.embeddings.insert` | Store raw vector embeddings *(requires embeddings tenant)* |
546
- | `client.embeddings.search` | Vector similarity search |
547
- | `client.embeddings.filter` | Retrieve embeddings by source or chunk IDs |
548
- | `client.embeddings.delete` | Delete embeddings by source or chunk IDs |
549
- | `client.metricsMetricsGet` | Retrieve platform metrics |
550
-
551
- > **Method Mapping:** `client.<group>.<method>` mirrors `api.hydradb.com/<group>/<method>`
552
- >
553
- > For example: `client.upload.knowledge()` → `POST /ingestion/upload_knowledge`
765
+ ## Passthrough fetch
554
766
 
555
- ---
767
+ Use `client.passthroughFetch()` for API endpoints that are not yet wrapped by the generated SDK. It uses the SDK's configured auth headers, base URL, timeout, retry, and fetch settings.
556
768
 
557
- ## Type Safety & IDE Support
769
+ ```ts
770
+ const response = await client.passthroughFetch("/metrics", {
771
+ method: "GET",
772
+ });
558
773
 
559
- The SDK provides exact type parity with the API:
774
+ const data = await response.json();
775
+ console.log(data);
776
+ ```
560
777
 
561
- - **Request parameters** — every field (required, optional, type, validation) is reflected in the method signature
562
- - **Response objects** — return types match the exact JSON schema from each endpoint
563
- - **Nested objects** — complex parameters and responses preserve their full structure
778
+ ## Error handling
564
779
 
565
- Your IDE will automatically provide autocompletion, type-checking, inline documentation, and compile-time validation for every method. Just hit **Cmd+Space / Ctrl+Space**.
780
+ ```ts
781
+ import { HydraDBError, HydraDBTimeoutError } from "@hydradb/sdk";
782
+
783
+ try {
784
+ await client.upload.knowledge({
785
+ tenant_id: TENANT_ID,
786
+ sub_tenant_id: SUB_TENANT_ID,
787
+ files: [{ path: "./missing.pdf" }],
788
+ });
789
+ } catch (error) {
790
+ if (error instanceof HydraDBTimeoutError) {
791
+ console.error("Request timed out", error);
792
+ } else if (error instanceof HydraDBError) {
793
+ console.error("Hydra DB API error", error.statusCode, error.body);
794
+ } else {
795
+ console.error("Unexpected error", error);
796
+ }
797
+ }
798
+ ```
799
+
800
+ ## SDK method reference
801
+
802
+ | SDK method | API path | Description |
803
+ |---|---|---|
804
+ | `client.tenant.create()` | `POST /tenants/create` | Create a standard or embeddings tenant |
805
+ | `client.tenant.getSubTenantIds()` | `GET /tenants/sub_tenant_ids` | List sub-tenant IDs |
806
+ | `client.tenant.getTenantIds()` | `GET /tenants/tenant_ids` | List tenant IDs |
807
+ | `client.tenant.getInfraStatus()` | `GET /tenants/infra/status` | Check tenant infrastructure |
808
+ | `client.tenant.monitor()` | `GET /tenants/monitor` | Get tenant stats |
809
+ | `client.tenant.deleteTenant()` | `DELETE /tenants/delete` | Delete a tenant |
810
+ | `client.upload.knowledge()` | `POST /ingestion/upload_knowledge` | Upload files or app knowledge |
811
+ | `client.upload.verifyProcessing()` | `POST /ingestion/verify_processing` | Check ingestion status |
812
+ | `client.upload.addMemory()` | `POST /memories/add_memory` | Add memories |
813
+ | `client.upload.deleteMemory()` | `DELETE /memories/delete_memory` | Delete one memory |
814
+ | `client.recall.fullRecall()` | `POST /recall/search` | Hybrid semantic and keyword recall |
815
+ | `client.recall.recallPreferences()` | `POST /recall/preferences` | Search preferences/memories |
816
+ | `client.recall.booleanRecall()` | `POST /recall/boolean` | Boolean/BM25 recall |
817
+ | `client.fetch.listData()` | `POST /fetch/list` | List knowledge or memories |
818
+ | `client.fetch.content()` | `POST /fetch/source` | Fetch source content or URL |
819
+ | `client.fetch.graphRelationsBySourceId()` | `GET /fetch/graph_relations` | Fetch graph relations |
820
+ | `client.data.delete()` | `POST /data/delete` | Delete sources |
821
+ | `client.graphHealth.getSuperNodes()` | `GET /graph_health/super_nodes` | Fetch graph super nodes |
822
+ | `client.embeddings.insert()` | `POST /embeddings/insert_raw_embeddings` | Insert raw embeddings |
823
+ | `client.embeddings.search()` | `POST /embeddings/search_raw_embeddings` | Search raw embeddings |
824
+ | `client.embeddings.filter()` | `POST /embeddings/filter_raw_embeddings` | Filter raw embeddings |
825
+ | `client.embeddings.delete()` | `DELETE /embeddings/delete` | Delete raw embeddings |
826
+ | `client.key.createApiKey()` | `POST /api_keys/create` | Create API key |
827
+ | `client.metricsMetricsGet()` | `GET /metrics` | Platform metrics |
828
+ | `client.passthroughFetch()` | any path | Authenticated passthrough request |
566
829
 
567
830
  ---
568
831
 
@@ -1,19 +1,20 @@
1
1
  /**
2
2
  * @example
3
3
  * {
4
- * source_id: "<str>",
5
4
  * tenant_id: "tenant_1234"
6
5
  * }
7
6
  */
8
7
  export interface GraphRelationsBySourceIdFetchRequest {
9
- /** The source ID to fetch relations for */
10
- source_id: string;
8
+ /** Source ID. If omitted, returns relations across the entire sub-tenant. */
9
+ source_id?: string | null;
11
10
  /** Unique identifier for the tenant/organization */
12
11
  tenant_id?: string;
13
12
  /** Whether to fetch relations for memories */
14
13
  is_memory?: boolean;
15
14
  /** Optional sub-tenant identifier used to organize data within a tenant. If omitted, the default sub-tenant created during tenant setup will be used. */
16
15
  sub_tenant_id?: string | null;
17
- /** Maximum number of relations to return */
16
+ /** Maximum number of relation groups to return */
18
17
  limit?: number;
18
+ /** Pagination cursor. Pass next_cursor from a previous response. */
19
+ cursor?: number | null;
19
20
  }
@@ -2,6 +2,10 @@ import type * as HydraDB from "../index.js";
2
2
  export interface SourceGraphRelationsResponse {
3
3
  /** List of relations retrieved */
4
4
  relations: (HydraDB.TripletWithEvidence | null)[];
5
+ /** Whether the result was truncated due to the limit */
6
+ is_truncated?: boolean | undefined;
7
+ /** Opaque cursor for fetching the next page. Pass this value as the 'cursor' query parameter to continue pagination. None when there are no more results. */
8
+ next_cursor?: (number | null) | undefined;
5
9
  /** Indicates whether the request was successful */
6
10
  success?: boolean | undefined;
7
11
  /** Response message describing the operation result */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydradb/sdk",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "The official TypeScript SDK for the Hydra DB platform.",
5
5
  "author": "Nishkarsh Shrivastava <nishkarsh@hydradb.com>",
6
6
  "main": "./dist/index.js",