nadir-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/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # Nadir Node.js SDK
2
+
3
+ Official Node.js/TypeScript client for the [Nadir](https://getnadir.dev) LLM router — intelligent model routing that cuts API costs 30-60%.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install nadir-sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { NadirClient } from "nadir-sdk";
15
+
16
+ const client = new NadirClient({ apiKey: "ndr_..." });
17
+
18
+ // Chat completion — Nadir picks the optimal model
19
+ const response = await client.chat.completions.create({
20
+ messages: [{ role: "user", content: "What is 2+2?" }],
21
+ });
22
+ console.log(response.choices[0].message?.content);
23
+
24
+ // See which model was selected and why
25
+ console.log(response.nadir_metadata?.tier); // "simple"
26
+ console.log(response.nadir_metadata?.selected_model); // "gpt-4o-mini"
27
+ console.log(response.nadir_metadata?.complexity_score); // 0.12
28
+ ```
29
+
30
+ ## Streaming
31
+
32
+ ```typescript
33
+ const stream = await client.chat.completions.create({
34
+ messages: [{ role: "user", content: "Tell me a story" }],
35
+ stream: true,
36
+ });
37
+
38
+ for await (const chunk of stream) {
39
+ process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
40
+ }
41
+ ```
42
+
43
+ ## Model Recommendation (no LLM call)
44
+
45
+ ```typescript
46
+ const rec = await client.recommend({
47
+ prompt: "Explain quantum entanglement in detail",
48
+ });
49
+ console.log(rec);
50
+ ```
51
+
52
+ ## Advanced: Fallback & Routing Control
53
+
54
+ ```typescript
55
+ const response = await client.chat.completions.create({
56
+ messages: [{ role: "user", content: "Complex analysis..." }],
57
+ route: "fallback", // enable auto-fallback
58
+ fallback_models: ["claude-sonnet-4-20250514", "gpt-4o"], // explicit fallback chain
59
+ layers: { routing: true, optimize: true }, // per-request feature toggles
60
+ reasoning: { effort: "high" }, // reasoning token support
61
+ });
62
+ ```
63
+
64
+ ## Environment Variables
65
+
66
+ | Variable | Description | Default |
67
+ |----------|-------------|---------|
68
+ | `NADIR_API_KEY` | API key (fallback if not passed to constructor) | — |
69
+ | `NADIR_BASE_URL` | API base URL | `https://api.getnadir.dev` |
70
+
71
+ ## OpenAI Drop-in Compatibility
72
+
73
+ Nadir's API is OpenAI-compatible. You can also use the OpenAI SDK directly:
74
+
75
+ ```typescript
76
+ import OpenAI from "openai";
77
+
78
+ const client = new OpenAI({
79
+ baseURL: "https://api.getnadir.dev/v1",
80
+ apiKey: "ndr_...",
81
+ });
82
+
83
+ const response = await client.chat.completions.create({
84
+ model: "auto",
85
+ messages: [{ role: "user", content: "Hello" }],
86
+ });
87
+ ```
88
+
89
+ The Nadir SDK adds typed access to routing metadata, recommendations, smart export, and clustering that the OpenAI SDK can't reach.
90
+
91
+ ## Requirements
92
+
93
+ - Node.js >= 18 (uses native `fetch`)
94
+ - Zero runtime dependencies
package/dist/index.cjs ADDED
@@ -0,0 +1,337 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ APIError: () => APIError,
24
+ AuthenticationError: () => AuthenticationError,
25
+ ConnectionError: () => ConnectionError,
26
+ NadirClient: () => NadirClient,
27
+ NadirError: () => NadirError,
28
+ RateLimitError: () => RateLimitError,
29
+ Stream: () => Stream
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/errors.ts
34
+ var NadirError = class extends Error {
35
+ status;
36
+ body;
37
+ errorType;
38
+ constructor(message, options = {}) {
39
+ super(message);
40
+ this.name = "NadirError";
41
+ this.status = options.status ?? 0;
42
+ this.body = options.body;
43
+ this.errorType = options.errorType;
44
+ }
45
+ };
46
+ var AuthenticationError = class extends NadirError {
47
+ constructor(message, body) {
48
+ super(message, { status: 401, body });
49
+ this.name = "AuthenticationError";
50
+ }
51
+ };
52
+ var RateLimitError = class extends NadirError {
53
+ retryAfter;
54
+ constructor(message, options = {}) {
55
+ super(message, { status: 429, body: options.body });
56
+ this.name = "RateLimitError";
57
+ this.retryAfter = options.retryAfter;
58
+ }
59
+ };
60
+ var APIError = class extends NadirError {
61
+ constructor(message, status, body) {
62
+ super(message, { status, body });
63
+ this.name = "APIError";
64
+ }
65
+ };
66
+ var ConnectionError = class extends NadirError {
67
+ constructor(message) {
68
+ super(message, { status: 0 });
69
+ this.name = "ConnectionError";
70
+ }
71
+ };
72
+
73
+ // src/streaming.ts
74
+ var Stream = class {
75
+ reader;
76
+ decoder = new TextDecoder();
77
+ buffer = "";
78
+ done = false;
79
+ parse;
80
+ constructor(body, parse) {
81
+ this.reader = body.getReader();
82
+ this.parse = parse;
83
+ }
84
+ async *[Symbol.asyncIterator]() {
85
+ try {
86
+ while (!this.done) {
87
+ const { value, done } = await this.reader.read();
88
+ if (done) {
89
+ this.done = true;
90
+ yield* this.processBuffer(true);
91
+ break;
92
+ }
93
+ this.buffer += this.decoder.decode(value, { stream: true });
94
+ yield* this.processBuffer(false);
95
+ }
96
+ } finally {
97
+ this.reader.releaseLock();
98
+ }
99
+ }
100
+ *processBuffer(flush) {
101
+ const lines = this.buffer.split("\n");
102
+ if (!flush) {
103
+ this.buffer = lines.pop() ?? "";
104
+ } else {
105
+ this.buffer = "";
106
+ }
107
+ for (const line of lines) {
108
+ const trimmed = line.trim();
109
+ if (!trimmed || !trimmed.startsWith("data: ")) {
110
+ continue;
111
+ }
112
+ const payload = trimmed.slice(6);
113
+ if (payload === "[DONE]") {
114
+ this.done = true;
115
+ return;
116
+ }
117
+ try {
118
+ const data = JSON.parse(payload);
119
+ yield this.parse(data);
120
+ } catch {
121
+ }
122
+ }
123
+ }
124
+ };
125
+ function parseChatCompletionChunk(data) {
126
+ return data;
127
+ }
128
+
129
+ // src/client.ts
130
+ var DEFAULT_BASE_URL = "https://api.getnadir.dev";
131
+ var SDK_VERSION = "0.1.0";
132
+ var Completions = class {
133
+ constructor(client) {
134
+ this.client = client;
135
+ }
136
+ client;
137
+ async create(params) {
138
+ if (params.stream) {
139
+ const response = await this.client._rawRequest(
140
+ "POST",
141
+ "/v1/chat/completions",
142
+ params
143
+ );
144
+ if (!response.body) {
145
+ throw new ConnectionError("Response body is null \u2014 streaming not supported");
146
+ }
147
+ return new Stream(
148
+ response.body,
149
+ parseChatCompletionChunk
150
+ );
151
+ }
152
+ return this.client._request(
153
+ "POST",
154
+ "/v1/chat/completions",
155
+ params
156
+ );
157
+ }
158
+ };
159
+ var Chat = class {
160
+ completions;
161
+ constructor(client) {
162
+ this.completions = new Completions(client);
163
+ }
164
+ };
165
+ var SmartExport = class {
166
+ constructor(client) {
167
+ this.client = client;
168
+ }
169
+ client;
170
+ async status() {
171
+ return this.client._request("GET", "/v1/smart-export/status");
172
+ }
173
+ async train(clusterId, options = {}) {
174
+ return this.client._request(
175
+ "POST",
176
+ `/v1/smart-export/clusters/${clusterId}/train`,
177
+ { base_model: options.baseModel ?? "gpt-4o-mini" }
178
+ );
179
+ }
180
+ async exportData(clusterId, options = {}) {
181
+ return this.client._request(
182
+ "POST",
183
+ `/v1/smart-export/clusters/${clusterId}/export`,
184
+ { format: options.format ?? "jsonl" }
185
+ );
186
+ }
187
+ async listJobs() {
188
+ return this.client._request("GET", "/v1/smart-export/jobs");
189
+ }
190
+ async getJob(jobId) {
191
+ return this.client._request("GET", `/v1/smart-export/jobs/${jobId}`);
192
+ }
193
+ async cancelJob(jobId) {
194
+ return this.client._request("DELETE", `/v1/smart-export/jobs/${jobId}`);
195
+ }
196
+ };
197
+ var Clustering = class {
198
+ constructor(client) {
199
+ this.client = client;
200
+ }
201
+ client;
202
+ async classify(prompt) {
203
+ return this.client._request("POST", "/v1/clustering/classify", { prompt });
204
+ }
205
+ async listClusters() {
206
+ return this.client._request("GET", "/v1/clustering/clusters");
207
+ }
208
+ async createCluster(params) {
209
+ return this.client._request("POST", "/v1/clustering/clusters", params);
210
+ }
211
+ };
212
+ var Organizations = class {
213
+ constructor(client) {
214
+ this.client = client;
215
+ }
216
+ client;
217
+ async list() {
218
+ return this.client._request("GET", "/v1/organizations");
219
+ }
220
+ async create(params) {
221
+ return this.client._request("POST", "/v1/organizations", params);
222
+ }
223
+ async get(orgId) {
224
+ return this.client._request("GET", `/v1/organizations/${orgId}`);
225
+ }
226
+ };
227
+ var NadirClient = class {
228
+ apiKey;
229
+ baseURL;
230
+ timeout;
231
+ /** OpenAI-compatible chat completions namespace. */
232
+ chat;
233
+ /** Smart Export / distillation endpoints. */
234
+ smartExport;
235
+ /** Prompt clustering endpoints. */
236
+ clustering;
237
+ /** Organization management endpoints. */
238
+ organizations;
239
+ constructor(options = {}) {
240
+ this.apiKey = options.apiKey ?? (typeof process !== "undefined" ? process.env.NADIR_API_KEY ?? "" : "");
241
+ this.baseURL = (options.baseURL ?? (typeof process !== "undefined" ? process.env.NADIR_BASE_URL ?? DEFAULT_BASE_URL : DEFAULT_BASE_URL)).replace(/\/+$/, "");
242
+ this.timeout = options.timeout ?? 12e4;
243
+ this.chat = new Chat(this);
244
+ this.smartExport = new SmartExport(this);
245
+ this.clustering = new Clustering(this);
246
+ this.organizations = new Organizations(this);
247
+ }
248
+ // ── Convenience methods ─────────────────────────────────────────
249
+ /** Get a model recommendation without making an LLM call. */
250
+ async recommend(params) {
251
+ return this._request("POST", "/v1/public/recommendation", params);
252
+ }
253
+ /** List available models. */
254
+ async listModels() {
255
+ return this._request("GET", "/v1/models");
256
+ }
257
+ /** List user presets. */
258
+ async listPresets() {
259
+ return this._request("GET", "/v1/user/presets");
260
+ }
261
+ /** Check API health. */
262
+ async health() {
263
+ return this._request("GET", "/health");
264
+ }
265
+ // ── HTTP internals (public for namespace classes) ───────────────
266
+ /** @internal */
267
+ async _request(method, path, body) {
268
+ const response = await this._rawRequest(method, path, body);
269
+ return await response.json();
270
+ }
271
+ /** @internal — returns the raw Response (for streaming). */
272
+ async _rawRequest(method, path, body) {
273
+ const url = `${this.baseURL}${path}`;
274
+ const headers = {
275
+ "Content-Type": "application/json",
276
+ "X-API-Key": this.apiKey,
277
+ "User-Agent": `nadir-node/${SDK_VERSION}`
278
+ };
279
+ let response;
280
+ try {
281
+ response = await fetch(url, {
282
+ method,
283
+ headers,
284
+ body: body != null ? JSON.stringify(body) : void 0,
285
+ signal: AbortSignal.timeout(this.timeout)
286
+ });
287
+ } catch (err) {
288
+ if (err instanceof Error && err.name === "TimeoutError") {
289
+ throw new ConnectionError(
290
+ `Request timed out after ${this.timeout}ms: ${method} ${path}`
291
+ );
292
+ }
293
+ throw new ConnectionError(
294
+ `Failed to connect to ${this.baseURL}: ${err instanceof Error ? err.message : String(err)}`
295
+ );
296
+ }
297
+ if (response.ok) {
298
+ return response;
299
+ }
300
+ let errorBody;
301
+ let detail = "";
302
+ try {
303
+ errorBody = await response.json();
304
+ if (typeof errorBody === "object" && errorBody !== null) {
305
+ const obj = errorBody;
306
+ detail = obj.detail ?? obj.error?.message ?? "";
307
+ }
308
+ } catch {
309
+ detail = await response.text().catch(() => "");
310
+ }
311
+ if (!detail) {
312
+ detail = `HTTP ${response.status}`;
313
+ }
314
+ if (response.status === 401) {
315
+ throw new AuthenticationError(detail, errorBody);
316
+ }
317
+ if (response.status === 429) {
318
+ const retryAfter = response.headers.get("retry-after");
319
+ throw new RateLimitError(detail, {
320
+ body: errorBody,
321
+ retryAfter: retryAfter ? parseFloat(retryAfter) : void 0
322
+ });
323
+ }
324
+ throw new APIError(detail, response.status, errorBody);
325
+ }
326
+ };
327
+ // Annotate the CommonJS export names for ESM import in node:
328
+ 0 && (module.exports = {
329
+ APIError,
330
+ AuthenticationError,
331
+ ConnectionError,
332
+ NadirClient,
333
+ NadirError,
334
+ RateLimitError,
335
+ Stream
336
+ });
337
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/streaming.ts","../src/client.ts"],"sourcesContent":["export { NadirClient } from \"./client.js\";\nexport type { NadirClientOptions } from \"./client.js\";\n\nexport { Stream } from \"./streaming.js\";\n\nexport {\n NadirError,\n AuthenticationError,\n RateLimitError,\n APIError,\n ConnectionError,\n} from \"./errors.js\";\n\nexport type {\n ChatMessage,\n ChatCompletionCreateParams,\n ReasoningConfig,\n ProviderPreferences,\n ChatCompletion,\n ChatCompletionChunk,\n Choice,\n Message,\n Usage,\n NadirMetadata,\n CreateClusterParams,\n CreateOrganizationParams,\n RecommendParams,\n} from \"./types.js\";\n","/** Base error for all Nadir API errors. */\nexport class NadirError extends Error {\n readonly status: number;\n readonly body: unknown;\n readonly errorType: string | undefined;\n\n constructor(\n message: string,\n options: { status?: number; body?: unknown; errorType?: string } = {},\n ) {\n super(message);\n this.name = \"NadirError\";\n this.status = options.status ?? 0;\n this.body = options.body;\n this.errorType = options.errorType;\n }\n}\n\n/** Raised on 401 Unauthorized — invalid or missing API key. */\nexport class AuthenticationError extends NadirError {\n constructor(message: string, body?: unknown) {\n super(message, { status: 401, body });\n this.name = \"AuthenticationError\";\n }\n}\n\n/** Raised on 429 Too Many Requests. */\nexport class RateLimitError extends NadirError {\n readonly retryAfter: number | undefined;\n\n constructor(\n message: string,\n options: { body?: unknown; retryAfter?: number } = {},\n ) {\n super(message, { status: 429, body: options.body });\n this.name = \"RateLimitError\";\n this.retryAfter = options.retryAfter;\n }\n}\n\n/** Raised on server errors (4xx/5xx) not covered by a more specific class. */\nexport class APIError extends NadirError {\n constructor(message: string, status: number, body?: unknown) {\n super(message, { status, body });\n this.name = \"APIError\";\n }\n}\n\n/** Raised when the SDK cannot reach the Nadir API. */\nexport class ConnectionError extends NadirError {\n constructor(message: string) {\n super(message, { status: 0 });\n this.name = \"ConnectionError\";\n }\n}\n","import type { ChatCompletionChunk } from \"./types.js\";\n\n/**\n * Async iterable wrapper around an SSE response stream.\n *\n * Usage:\n * ```ts\n * for await (const chunk of stream) {\n * process.stdout.write(chunk.choices[0]?.delta?.content ?? \"\");\n * }\n * ```\n */\nexport class Stream<T> implements AsyncIterable<T> {\n private reader: ReadableStreamDefaultReader<Uint8Array>;\n private decoder = new TextDecoder();\n private buffer = \"\";\n private done = false;\n private parse: (data: unknown) => T;\n\n constructor(\n body: ReadableStream<Uint8Array>,\n parse: (data: unknown) => T,\n ) {\n this.reader = body.getReader();\n this.parse = parse;\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<T> {\n try {\n while (!this.done) {\n const { value, done } = await this.reader.read();\n if (done) {\n this.done = true;\n // Process any remaining buffer\n yield* this.processBuffer(true);\n break;\n }\n\n this.buffer += this.decoder.decode(value, { stream: true });\n yield* this.processBuffer(false);\n }\n } finally {\n this.reader.releaseLock();\n }\n }\n\n private *processBuffer(flush: boolean): Generator<T> {\n const lines = this.buffer.split(\"\\n\");\n\n // Keep the last incomplete line in the buffer (unless flushing)\n if (!flush) {\n this.buffer = lines.pop() ?? \"\";\n } else {\n this.buffer = \"\";\n }\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || !trimmed.startsWith(\"data: \")) {\n continue;\n }\n const payload = trimmed.slice(6);\n if (payload === \"[DONE]\") {\n this.done = true;\n return;\n }\n try {\n const data = JSON.parse(payload);\n yield this.parse(data);\n } catch {\n // Skip malformed JSON lines\n }\n }\n }\n}\n\n/** Identity parser for ChatCompletionChunk (the API returns the correct shape). */\nexport function parseChatCompletionChunk(data: unknown): ChatCompletionChunk {\n return data as ChatCompletionChunk;\n}\n","/**\n * Nadir Node.js SDK — OpenAI-compatible client for the Nadir LLM router.\n *\n * @example\n * ```ts\n * import { NadirClient } from \"nadir-sdk\";\n *\n * const client = new NadirClient({ apiKey: \"ndr_...\" });\n *\n * // Chat completion — Nadir picks the optimal model\n * const response = await client.chat.completions.create({\n * messages: [{ role: \"user\", content: \"Hello!\" }],\n * });\n * console.log(response.choices[0].message?.content);\n *\n * // Streaming\n * const stream = await client.chat.completions.create({\n * messages: [{ role: \"user\", content: \"Tell me a story\" }],\n * stream: true,\n * });\n * for await (const chunk of stream) {\n * process.stdout.write(chunk.choices[0]?.delta?.content ?? \"\");\n * }\n * ```\n */\n\nimport {\n APIError,\n AuthenticationError,\n ConnectionError,\n RateLimitError,\n} from \"./errors.js\";\nimport { Stream, parseChatCompletionChunk } from \"./streaming.js\";\nimport type {\n ChatCompletion,\n ChatCompletionChunk,\n ChatCompletionCreateParams,\n CreateClusterParams,\n CreateOrganizationParams,\n RecommendParams,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.getnadir.dev\";\nconst SDK_VERSION = \"0.1.0\";\n\n// ---------------------------------------------------------------------------\n// Client options\n// ---------------------------------------------------------------------------\n\nexport interface NadirClientOptions {\n /** Nadir API key (`ndr_...`). Falls back to `NADIR_API_KEY` env var. */\n apiKey?: string;\n /** API base URL. Falls back to `NADIR_BASE_URL` env var. */\n baseURL?: string;\n /** Request timeout in milliseconds (default 120_000). */\n timeout?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Namespace classes\n// ---------------------------------------------------------------------------\n\nclass Completions {\n constructor(private client: NadirClient) {}\n\n /** Create a chat completion (non-streaming). */\n async create(\n params: ChatCompletionCreateParams & { stream?: false },\n ): Promise<ChatCompletion>;\n /** Create a streaming chat completion. */\n async create(\n params: ChatCompletionCreateParams & { stream: true },\n ): Promise<Stream<ChatCompletionChunk>>;\n /** Create a chat completion (overload resolution). */\n async create(\n params: ChatCompletionCreateParams,\n ): Promise<ChatCompletion | Stream<ChatCompletionChunk>>;\n async create(\n params: ChatCompletionCreateParams,\n ): Promise<ChatCompletion | Stream<ChatCompletionChunk>> {\n if (params.stream) {\n const response = await this.client._rawRequest(\n \"POST\",\n \"/v1/chat/completions\",\n params,\n );\n\n if (!response.body) {\n throw new ConnectionError(\"Response body is null — streaming not supported\");\n }\n\n return new Stream<ChatCompletionChunk>(\n response.body,\n parseChatCompletionChunk,\n );\n }\n\n return this.client._request<ChatCompletion>(\n \"POST\",\n \"/v1/chat/completions\",\n params,\n );\n }\n}\n\nclass Chat {\n readonly completions: Completions;\n constructor(client: NadirClient) {\n this.completions = new Completions(client);\n }\n}\n\nclass SmartExport {\n constructor(private client: NadirClient) {}\n\n async status(): Promise<Record<string, unknown>> {\n return this.client._request(\"GET\", \"/v1/smart-export/status\");\n }\n\n async train(\n clusterId: string,\n options: { baseModel?: string } = {},\n ): Promise<Record<string, unknown>> {\n return this.client._request(\n \"POST\",\n `/v1/smart-export/clusters/${clusterId}/train`,\n { base_model: options.baseModel ?? \"gpt-4o-mini\" },\n );\n }\n\n async exportData(\n clusterId: string,\n options: { format?: string } = {},\n ): Promise<Record<string, unknown>> {\n return this.client._request(\n \"POST\",\n `/v1/smart-export/clusters/${clusterId}/export`,\n { format: options.format ?? \"jsonl\" },\n );\n }\n\n async listJobs(): Promise<Record<string, unknown>> {\n return this.client._request(\"GET\", \"/v1/smart-export/jobs\");\n }\n\n async getJob(jobId: string): Promise<Record<string, unknown>> {\n return this.client._request(\"GET\", `/v1/smart-export/jobs/${jobId}`);\n }\n\n async cancelJob(jobId: string): Promise<Record<string, unknown>> {\n return this.client._request(\"DELETE\", `/v1/smart-export/jobs/${jobId}`);\n }\n}\n\nclass Clustering {\n constructor(private client: NadirClient) {}\n\n async classify(prompt: string): Promise<Record<string, unknown>> {\n return this.client._request(\"POST\", \"/v1/clustering/classify\", { prompt });\n }\n\n async listClusters(): Promise<Record<string, unknown>> {\n return this.client._request(\"GET\", \"/v1/clustering/clusters\");\n }\n\n async createCluster(params: CreateClusterParams): Promise<Record<string, unknown>> {\n return this.client._request(\"POST\", \"/v1/clustering/clusters\", params);\n }\n}\n\nclass Organizations {\n constructor(private client: NadirClient) {}\n\n async list(): Promise<Record<string, unknown>> {\n return this.client._request(\"GET\", \"/v1/organizations\");\n }\n\n async create(params: CreateOrganizationParams): Promise<Record<string, unknown>> {\n return this.client._request(\"POST\", \"/v1/organizations\", params);\n }\n\n async get(orgId: string): Promise<Record<string, unknown>> {\n return this.client._request(\"GET\", `/v1/organizations/${orgId}`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Main client\n// ---------------------------------------------------------------------------\n\nexport class NadirClient {\n private readonly apiKey: string;\n private readonly baseURL: string;\n private readonly timeout: number;\n\n /** OpenAI-compatible chat completions namespace. */\n readonly chat: Chat;\n /** Smart Export / distillation endpoints. */\n readonly smartExport: SmartExport;\n /** Prompt clustering endpoints. */\n readonly clustering: Clustering;\n /** Organization management endpoints. */\n readonly organizations: Organizations;\n\n constructor(options: NadirClientOptions = {}) {\n this.apiKey =\n options.apiKey ??\n (typeof process !== \"undefined\" ? process.env.NADIR_API_KEY ?? \"\" : \"\");\n this.baseURL = (\n options.baseURL ??\n (typeof process !== \"undefined\"\n ? process.env.NADIR_BASE_URL ?? DEFAULT_BASE_URL\n : DEFAULT_BASE_URL)\n ).replace(/\\/+$/, \"\");\n this.timeout = options.timeout ?? 120_000;\n\n this.chat = new Chat(this);\n this.smartExport = new SmartExport(this);\n this.clustering = new Clustering(this);\n this.organizations = new Organizations(this);\n }\n\n // ── Convenience methods ─────────────────────────────────────────\n\n /** Get a model recommendation without making an LLM call. */\n async recommend(params: RecommendParams): Promise<Record<string, unknown>> {\n return this._request(\"POST\", \"/v1/public/recommendation\", params);\n }\n\n /** List available models. */\n async listModels(): Promise<Record<string, unknown>> {\n return this._request(\"GET\", \"/v1/models\");\n }\n\n /** List user presets. */\n async listPresets(): Promise<Record<string, unknown>> {\n return this._request(\"GET\", \"/v1/user/presets\");\n }\n\n /** Check API health. */\n async health(): Promise<Record<string, unknown>> {\n return this._request(\"GET\", \"/health\");\n }\n\n // ── HTTP internals (public for namespace classes) ───────────────\n\n /** @internal */\n async _request<T = Record<string, unknown>>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const response = await this._rawRequest(method, path, body);\n return (await response.json()) as T;\n }\n\n /** @internal — returns the raw Response (for streaming). */\n async _rawRequest(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<Response> {\n const url = `${this.baseURL}${path}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": this.apiKey,\n \"User-Agent\": `nadir-node/${SDK_VERSION}`,\n };\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers,\n body: body != null ? JSON.stringify(body) : undefined,\n signal: AbortSignal.timeout(this.timeout),\n });\n } catch (err: unknown) {\n if (err instanceof Error && err.name === \"TimeoutError\") {\n throw new ConnectionError(\n `Request timed out after ${this.timeout}ms: ${method} ${path}`,\n );\n }\n throw new ConnectionError(\n `Failed to connect to ${this.baseURL}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n if (response.ok) {\n return response;\n }\n\n // Parse error body\n let errorBody: unknown;\n let detail = \"\";\n try {\n errorBody = await response.json();\n if (typeof errorBody === \"object\" && errorBody !== null) {\n const obj = errorBody as Record<string, unknown>;\n detail =\n (obj.detail as string) ??\n ((obj.error as Record<string, unknown>)?.message as string) ??\n \"\";\n }\n } catch {\n detail = await response.text().catch(() => \"\");\n }\n\n if (!detail) {\n detail = `HTTP ${response.status}`;\n }\n\n if (response.status === 401) {\n throw new AuthenticationError(detail, errorBody);\n }\n if (response.status === 429) {\n const retryAfter = response.headers.get(\"retry-after\");\n throw new RateLimitError(detail, {\n body: errorBody,\n retryAfter: retryAfter ? parseFloat(retryAfter) : undefined,\n });\n }\n throw new APIError(detail, response.status, errorBody);\n }\n}\n"],"mappings":"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","names":[]}