@layerum-team/rag-sdk 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/dist/index.js ADDED
@@ -0,0 +1,419 @@
1
+ // src/error.ts
2
+ var LayerumApiError = class extends Error {
3
+ status;
4
+ details;
5
+ path;
6
+ method;
7
+ constructor(message, status, details, method, path) {
8
+ super(message);
9
+ this.name = "LayerumApiError";
10
+ this.status = status;
11
+ this.details = details;
12
+ this.method = method;
13
+ this.path = path;
14
+ }
15
+ };
16
+
17
+ // src/http.ts
18
+ function normalizeErrorMessage(payload, status) {
19
+ const fallback = `Request failed (${status})`;
20
+ if (!payload || typeof payload !== "object") return fallback;
21
+ const message = payload.message;
22
+ if (Array.isArray(message)) {
23
+ return message.map((item) => typeof item === "string" ? item : JSON.stringify(item)).join(", ");
24
+ }
25
+ if (typeof message === "string") return message;
26
+ if (message && typeof message === "object") {
27
+ const nested = message.message;
28
+ if (typeof nested === "string") return nested;
29
+ }
30
+ return fallback;
31
+ }
32
+ function shouldRetry(status, method, attempt, maxRetries) {
33
+ if (attempt >= maxRetries) return false;
34
+ if (method === "POST" || method === "PATCH" || method === "DELETE") return false;
35
+ return status === 429 || status >= 500;
36
+ }
37
+ function sleep(ms) {
38
+ return new Promise((resolve) => setTimeout(resolve, ms));
39
+ }
40
+ var HttpClient = class {
41
+ baseUrl;
42
+ apiKey;
43
+ timeoutMs;
44
+ maxRetries;
45
+ constructor(options) {
46
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
47
+ this.apiKey = options.apiKey;
48
+ this.timeoutMs = options.timeoutMs;
49
+ this.maxRetries = options.maxRetries;
50
+ }
51
+ async request(path, options = {}) {
52
+ const method = options.method ?? "GET";
53
+ let attempt = 0;
54
+ while (true) {
55
+ const controller = new AbortController();
56
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
57
+ try {
58
+ const response = await fetch(`${this.baseUrl}${path}`, {
59
+ method,
60
+ headers: options.isFormData ? { Authorization: `Bearer ${this.apiKey}` } : {
61
+ "Content-Type": "application/json",
62
+ Authorization: `Bearer ${this.apiKey}`
63
+ },
64
+ body: options.body === void 0 ? void 0 : options.isFormData ? options.body : JSON.stringify(options.body),
65
+ signal: controller.signal
66
+ });
67
+ if (!response.ok) {
68
+ const payload = await response.json().catch(() => null);
69
+ const message = normalizeErrorMessage(payload, response.status);
70
+ if (shouldRetry(response.status, method, attempt, this.maxRetries)) {
71
+ attempt += 1;
72
+ await sleep(250 * attempt);
73
+ continue;
74
+ }
75
+ throw new LayerumApiError(
76
+ message,
77
+ response.status,
78
+ payload?.details,
79
+ method,
80
+ path
81
+ );
82
+ }
83
+ if (response.status === 204) return void 0;
84
+ return await response.json();
85
+ } catch (error) {
86
+ if (error instanceof LayerumApiError) throw error;
87
+ const message = error instanceof Error ? error.message : "Unknown network error";
88
+ throw new LayerumApiError(
89
+ `Network error while ${method} ${path}: ${message}`,
90
+ 0,
91
+ error,
92
+ method,
93
+ path
94
+ );
95
+ } finally {
96
+ clearTimeout(timeout);
97
+ }
98
+ }
99
+ }
100
+ };
101
+
102
+ // src/client.ts
103
+ function sleep2(ms) {
104
+ return new Promise((resolve) => setTimeout(resolve, ms));
105
+ }
106
+ function toBlob(file) {
107
+ if (typeof Blob !== "undefined" && file instanceof Blob) return file;
108
+ if (file instanceof ArrayBuffer) return new Blob([file]);
109
+ if (file instanceof Uint8Array) {
110
+ return new Blob([file]);
111
+ }
112
+ return new Blob([file]);
113
+ }
114
+ function unwrapItems(payload) {
115
+ if (!payload) return [];
116
+ if (Array.isArray(payload)) return payload;
117
+ return payload.items ?? [];
118
+ }
119
+ var EntitiesApi = class {
120
+ constructor(http) {
121
+ this.http = http;
122
+ }
123
+ async list(parentId) {
124
+ const payload = await this.http.request("/v1/entities");
125
+ const items = unwrapItems(payload);
126
+ if (!parentId) return items;
127
+ return items.filter(
128
+ (item) => (item.parentId ?? item.parent_id ?? null) === parentId
129
+ );
130
+ }
131
+ async tree() {
132
+ const payload = await this.http.request("/v1/entities/tree");
133
+ return unwrapItems(payload);
134
+ }
135
+ create(input) {
136
+ return this.http.request("/v1/entities", {
137
+ method: "POST",
138
+ body: {
139
+ entity_type_id: input.entityTypeId,
140
+ name: input.name,
141
+ description: input.description,
142
+ parent_id: input.parentId,
143
+ metadata: input.metadata
144
+ }
145
+ });
146
+ }
147
+ remove(entityId) {
148
+ return this.http.request(`/v1/entities/${entityId}`, {
149
+ method: "DELETE"
150
+ });
151
+ }
152
+ };
153
+ var EntityTypesApi = class {
154
+ constructor(http) {
155
+ this.http = http;
156
+ }
157
+ async list() {
158
+ const payload = await this.http.request(
159
+ "/v1/entity-types"
160
+ );
161
+ return unwrapItems(payload);
162
+ }
163
+ get(entityTypeId) {
164
+ return this.http.request(`/v1/entity-types/${entityTypeId}`);
165
+ }
166
+ create(input) {
167
+ return this.http.request("/v1/entity-types", {
168
+ method: "POST",
169
+ body: {
170
+ name: input.name,
171
+ label: input.label
172
+ }
173
+ });
174
+ }
175
+ remove(entityTypeId) {
176
+ return this.http.request(
177
+ `/v1/entity-types/${entityTypeId}`,
178
+ {
179
+ method: "DELETE"
180
+ }
181
+ );
182
+ }
183
+ };
184
+ var DocumentsApi = class {
185
+ constructor(http) {
186
+ this.http = http;
187
+ }
188
+ async list(input) {
189
+ const path = input.allInWorkspace ? "/v1/documents" : `/v1/entities/${input.entityId}/documents`;
190
+ const payload = await this.http.request(path);
191
+ return unwrapItems(payload);
192
+ }
193
+ get(entityId, sourceId) {
194
+ return this.http.request(
195
+ `/v1/entities/${entityId}/documents/${sourceId}`
196
+ );
197
+ }
198
+ upload(input) {
199
+ const form = new FormData();
200
+ form.set("file", toBlob(input.file), input.fileName);
201
+ if (input.visibility) form.set("visibility", input.visibility);
202
+ if (input.modelConfigId) form.set("model_config_id", input.modelConfigId);
203
+ return this.http.request(
204
+ `/v1/entities/${input.entityId}/documents/upload`,
205
+ {
206
+ method: "POST",
207
+ body: form,
208
+ isFormData: true
209
+ }
210
+ );
211
+ }
212
+ uploadInit(input) {
213
+ return this.http.request(
214
+ `/v1/entities/${input.entityId}/documents/upload/init`,
215
+ {
216
+ method: "POST",
217
+ body: {
218
+ file_name: input.fileName,
219
+ file_size_bytes: input.fileSizeBytes,
220
+ content_type: input.contentType,
221
+ visibility: input.visibility,
222
+ model_config_id: input.modelConfigId
223
+ }
224
+ }
225
+ );
226
+ }
227
+ uploadComplete(input) {
228
+ return this.http.request(
229
+ `/v1/entities/${input.entityId}/documents/upload/complete`,
230
+ {
231
+ method: "POST",
232
+ body: {
233
+ upload_id: input.uploadId,
234
+ display_name: input.displayName,
235
+ visibility: input.visibility,
236
+ model_config_id: input.modelConfigId,
237
+ checksum: input.checksum
238
+ }
239
+ }
240
+ );
241
+ }
242
+ async uploadDirect(input) {
243
+ const blob = toBlob(input.file);
244
+ const contentType = input.contentType || blob.type || "application/octet-stream";
245
+ const initialized = await this.uploadInit({
246
+ entityId: input.entityId,
247
+ fileName: input.fileName,
248
+ fileSizeBytes: blob.size,
249
+ contentType,
250
+ visibility: input.visibility,
251
+ modelConfigId: input.modelConfigId
252
+ });
253
+ const form = new FormData();
254
+ for (const [key, value] of Object.entries(initialized.fields ?? {})) {
255
+ form.set(key, value);
256
+ }
257
+ form.set("file", blob, input.fileName);
258
+ const uploadRes = await fetch(initialized.uploadUrl, {
259
+ method: initialized.method ?? "POST",
260
+ body: form
261
+ });
262
+ if (!uploadRes.ok) {
263
+ throw new Error(`Direct upload failed with status ${uploadRes.status}`);
264
+ }
265
+ return this.uploadComplete({
266
+ entityId: input.entityId,
267
+ uploadId: initialized.uploadId,
268
+ displayName: input.fileName,
269
+ visibility: input.visibility,
270
+ modelConfigId: input.modelConfigId
271
+ });
272
+ }
273
+ createText(input) {
274
+ return this.http.request(`/v1/entities/${input.entityId}/documents`, {
275
+ method: "POST",
276
+ body: {
277
+ source_type: "text",
278
+ display_name: input.displayName,
279
+ raw_text: input.rawText,
280
+ visibility: input.visibility ?? "self_and_descendants",
281
+ model_config_id: input.modelConfigId
282
+ }
283
+ });
284
+ }
285
+ createUrl(input) {
286
+ return this.http.request(`/v1/entities/${input.entityId}/documents`, {
287
+ method: "POST",
288
+ body: {
289
+ source_type: "url",
290
+ display_name: input.displayName,
291
+ source_url: input.sourceUrl,
292
+ visibility: input.visibility ?? "self_and_descendants",
293
+ model_config_id: input.modelConfigId
294
+ }
295
+ });
296
+ }
297
+ remove(input) {
298
+ return this.http.request(
299
+ `/v1/entities/${input.entityId}/documents/${input.sourceId}`,
300
+ {
301
+ method: "DELETE"
302
+ }
303
+ );
304
+ }
305
+ };
306
+ var IngestionApi = class {
307
+ constructor(http, documentsApi) {
308
+ this.http = http;
309
+ this.documentsApi = documentsApi;
310
+ }
311
+ retry(input) {
312
+ return this.http.request(
313
+ `/v1/entities/${input.entityId}/documents/${input.sourceId}/retry`,
314
+ {
315
+ method: "POST"
316
+ }
317
+ );
318
+ }
319
+ error(input) {
320
+ return this.http.request(
321
+ `/v1/entities/${input.entityId}/documents/${input.sourceId}/error`
322
+ );
323
+ }
324
+ async waitUntilReady(input) {
325
+ const timeoutMs = input.timeoutMs ?? 12e4;
326
+ const intervalMs = input.intervalMs ?? 2e3;
327
+ const start = Date.now();
328
+ while (true) {
329
+ const source = await this.documentsApi.get(input.entityId, input.sourceId);
330
+ if (source.status === "completed") return source;
331
+ if (source.status === "failed") {
332
+ const details = await this.error({
333
+ entityId: input.entityId,
334
+ sourceId: input.sourceId
335
+ }).catch(() => null);
336
+ throw new Error(
337
+ `Ingestion failed for source ${input.sourceId}${details?.latestJob?.error_message ? `: ${details.latestJob.error_message}` : ""}`
338
+ );
339
+ }
340
+ if (Date.now() - start >= timeoutMs) {
341
+ throw new Error(
342
+ `Timed out waiting for source ${input.sourceId} to complete ingestion`
343
+ );
344
+ }
345
+ await sleep2(intervalMs);
346
+ }
347
+ }
348
+ };
349
+ var QueryApi = class {
350
+ constructor(http) {
351
+ this.http = http;
352
+ }
353
+ run(input) {
354
+ return this.http.request("/v1/query", {
355
+ method: "POST",
356
+ body: {
357
+ entityId: input.entityId,
358
+ modelConfigId: input.modelConfigId,
359
+ query: input.query,
360
+ topK: input.topK,
361
+ includeParentScopes: input.includeParentScopes
362
+ }
363
+ });
364
+ }
365
+ runDebug(input) {
366
+ return this.http.request("/v1/query/debug", {
367
+ method: "POST",
368
+ body: {
369
+ entityId: input.entityId,
370
+ modelConfigId: input.modelConfigId,
371
+ query: input.query,
372
+ topK: input.topK,
373
+ includeParentScopes: input.includeParentScopes
374
+ }
375
+ });
376
+ }
377
+ };
378
+ var ModelsApi = class {
379
+ constructor(http) {
380
+ this.http = http;
381
+ }
382
+ async listConfigs() {
383
+ const payload = await this.http.request("/v1/models");
384
+ return unwrapItems(payload);
385
+ }
386
+ async listDefinitions(input) {
387
+ const query = input?.type ? `?type=${encodeURIComponent(input.type)}` : "";
388
+ const payload = await this.http.request(
389
+ `/v1/model-definitions${query}`
390
+ );
391
+ return unwrapItems(payload);
392
+ }
393
+ };
394
+ var LayerumClient = class {
395
+ entities;
396
+ entityTypes;
397
+ documents;
398
+ ingestion;
399
+ query;
400
+ models;
401
+ constructor(options) {
402
+ const http = new HttpClient({
403
+ apiKey: options.apiKey,
404
+ baseUrl: options.baseUrl ?? "http://localhost:4000",
405
+ timeoutMs: options.timeoutMs ?? 3e4,
406
+ maxRetries: options.maxRetries ?? 2
407
+ });
408
+ this.entities = new EntitiesApi(http);
409
+ this.entityTypes = new EntityTypesApi(http);
410
+ this.documents = new DocumentsApi(http);
411
+ this.ingestion = new IngestionApi(http, this.documents);
412
+ this.query = new QueryApi(http);
413
+ this.models = new ModelsApi(http);
414
+ }
415
+ };
416
+ export {
417
+ LayerumApiError,
418
+ LayerumClient
419
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@layerum-team/rag-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official Layerum RAG SDK for TypeScript/Node.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "scripts": {
25
+ "build": "tsup src/index.ts --format esm,cjs --dts",
26
+ "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
27
+ "typecheck": "tsc --noEmit",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "keywords": [
31
+ "layerum",
32
+ "rag",
33
+ "sdk"
34
+ ],
35
+ "license": "Apache-2.0",
36
+ "devDependencies": {
37
+ "@types/node": "^22.10.0",
38
+ "tsup": "^8.3.5",
39
+ "typescript": "^5.7.2"
40
+ }
41
+ }