novelai-image-sdk 1.0.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,530 @@
1
+ // src/errors/index.ts
2
+ var NovelAIError = class extends Error {
3
+ constructor(message, statusCode) {
4
+ super(message);
5
+ this.name = "NovelAIError";
6
+ this.statusCode = statusCode;
7
+ }
8
+ };
9
+ var NovelAIAuthError = class extends NovelAIError {
10
+ constructor(message = "Authentication failed. Check your Persistent API Token (pst-...).") {
11
+ super(message, 401);
12
+ this.name = "NovelAIAuthError";
13
+ }
14
+ };
15
+ var NovelAIPaymentError = class extends NovelAIError {
16
+ constructor(message = "Insufficient Anlas credits for this generation.") {
17
+ super(message, 402);
18
+ this.name = "NovelAIPaymentError";
19
+ }
20
+ };
21
+ var NovelAIValidationError = class extends NovelAIError {
22
+ constructor(message, details) {
23
+ super(message, 400);
24
+ this.name = "NovelAIValidationError";
25
+ this.details = details;
26
+ }
27
+ };
28
+ var NovelAINetworkError = class extends NovelAIError {
29
+ constructor(message, cause) {
30
+ super(message);
31
+ this.name = "NovelAINetworkError";
32
+ this.cause = cause;
33
+ }
34
+ };
35
+ var NovelAIRateLimitError = class extends NovelAIError {
36
+ constructor(message = "Rate limit exceeded. Please wait before retrying.", retryAfter) {
37
+ super(message, 429);
38
+ this.name = "NovelAIRateLimitError";
39
+ this.retryAfter = retryAfter;
40
+ }
41
+ };
42
+ var NovelAIServerError = class extends NovelAIError {
43
+ constructor(message = "NovelAI server error. This may be due to payload schema issues.", correlationId) {
44
+ super(message, 500);
45
+ this.name = "NovelAIServerError";
46
+ this.correlationId = correlationId;
47
+ }
48
+ };
49
+
50
+ // src/utils/ZipParser.ts
51
+ function isNodeEnvironment() {
52
+ return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
53
+ }
54
+ async function extractImagesFromZip(data) {
55
+ const buffer = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
56
+ if (isNodeEnvironment()) {
57
+ return extractWithAdmZip(buffer);
58
+ } else {
59
+ return extractWithFflate(buffer);
60
+ }
61
+ }
62
+ async function extractWithAdmZip(buffer) {
63
+ const AdmZip = (await import('adm-zip')).default;
64
+ const zip = new AdmZip(Buffer.from(buffer));
65
+ const entries = zip.getEntries();
66
+ const images = [];
67
+ for (const entry of entries) {
68
+ if (entry.entryName.endsWith(".png") && !entry.isDirectory) {
69
+ const data = entry.getData();
70
+ images.push(new Uint8Array(data));
71
+ }
72
+ }
73
+ return images;
74
+ }
75
+ async function extractWithFflate(buffer) {
76
+ const { unzipSync } = await import('fflate');
77
+ const unzipped = unzipSync(buffer);
78
+ const images = [];
79
+ for (const [filename, data] of Object.entries(unzipped)) {
80
+ if (filename.endsWith(".png")) {
81
+ images.push(data);
82
+ }
83
+ }
84
+ return images;
85
+ }
86
+ function toBase64(data) {
87
+ if (isNodeEnvironment()) {
88
+ return Buffer.from(data).toString("base64");
89
+ } else {
90
+ let binary = "";
91
+ for (let i = 0; i < data.length; i++) {
92
+ binary += String.fromCharCode(data[i]);
93
+ }
94
+ return btoa(binary);
95
+ }
96
+ }
97
+ function toDataURL(data, mimeType = "image/png") {
98
+ return `data:${mimeType};base64,${toBase64(data)}`;
99
+ }
100
+
101
+ // src/network/APIClient.ts
102
+ var DEFAULT_BASE_URL = "https://image.novelai.net";
103
+ var DEFAULT_TIMEOUT = 6e4;
104
+ var GENERATE_ENDPOINT = "/ai/generate-image";
105
+ var APIClient = class {
106
+ constructor(config) {
107
+ this.token = config.token;
108
+ this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
109
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
110
+ }
111
+ /**
112
+ * Generate an image using the NovelAI API
113
+ */
114
+ async generateImage(payload) {
115
+ const url = `${this.baseUrl}${GENERATE_ENDPOINT}`;
116
+ const correlationId = this.generateCorrelationId();
117
+ const controller = new AbortController();
118
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
119
+ try {
120
+ const response = await fetch(url, {
121
+ method: "POST",
122
+ headers: {
123
+ "Authorization": `Bearer ${this.token}`,
124
+ "Content-Type": "application/json",
125
+ "Accept": "application/x-zip-compressed, application/json",
126
+ "x-correlation-id": correlationId
127
+ },
128
+ body: JSON.stringify(payload),
129
+ signal: controller.signal
130
+ });
131
+ clearTimeout(timeoutId);
132
+ if (!response.ok) {
133
+ await this.handleErrorResponse(response, correlationId);
134
+ }
135
+ const arrayBuffer = await response.arrayBuffer();
136
+ const images = await extractImagesFromZip(arrayBuffer);
137
+ return {
138
+ images,
139
+ metadata: {
140
+ seed: payload.parameters.seed,
141
+ prompt: payload.input,
142
+ model: payload.model,
143
+ sampler: payload.parameters.sampler,
144
+ steps: payload.parameters.steps,
145
+ scale: payload.parameters.scale
146
+ }
147
+ };
148
+ } catch (error) {
149
+ clearTimeout(timeoutId);
150
+ if (error instanceof NovelAIError) {
151
+ throw error;
152
+ }
153
+ if (error instanceof Error) {
154
+ if (error.name === "AbortError") {
155
+ throw new NovelAINetworkError(`Request timed out after ${this.timeout}ms`, error);
156
+ }
157
+ throw new NovelAINetworkError(`Network error: ${error.message}`, error);
158
+ }
159
+ throw new NovelAINetworkError("An unknown error occurred");
160
+ }
161
+ }
162
+ /**
163
+ * Handle non-2xx responses and throw appropriate errors
164
+ */
165
+ async handleErrorResponse(response, correlationId) {
166
+ let errorBody = null;
167
+ try {
168
+ errorBody = await response.json();
169
+ } catch {
170
+ }
171
+ const message = errorBody?.message ?? errorBody?.error ?? response.statusText;
172
+ switch (response.status) {
173
+ case 400:
174
+ throw new NovelAIValidationError(
175
+ `Bad request: ${message}`,
176
+ JSON.stringify(errorBody)
177
+ );
178
+ case 401:
179
+ case 403:
180
+ throw new NovelAIAuthError(message);
181
+ case 402:
182
+ throw new NovelAIPaymentError(message);
183
+ case 429: {
184
+ const retryAfter = response.headers.get("retry-after");
185
+ throw new NovelAIRateLimitError(message, retryAfter ? parseInt(retryAfter) : void 0);
186
+ }
187
+ case 500:
188
+ case 502:
189
+ case 503:
190
+ case 504:
191
+ throw new NovelAIServerError(
192
+ `Server error (${response.status}): ${message}. This may be caused by payload schema issues.`,
193
+ correlationId
194
+ );
195
+ default:
196
+ throw new NovelAIError(
197
+ `HTTP ${response.status}: ${message}`,
198
+ response.status
199
+ );
200
+ }
201
+ }
202
+ /**
203
+ * Generate a 6-character alphanumeric correlation ID for debugging
204
+ */
205
+ generateCorrelationId() {
206
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
207
+ let result = "";
208
+ for (let i = 0; i < 6; i++) {
209
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
210
+ }
211
+ return result;
212
+ }
213
+ };
214
+
215
+ // src/types/Model.ts
216
+ var NovelAIModel = /* @__PURE__ */ ((NovelAIModel2) => {
217
+ NovelAIModel2["V45_Full"] = "nai-diffusion-4-5-full";
218
+ NovelAIModel2["V4_Curated"] = "nai-diffusion-4-curated";
219
+ NovelAIModel2["Inpainting"] = "nai-diffusion-4-inpainting";
220
+ return NovelAIModel2;
221
+ })(NovelAIModel || {});
222
+
223
+ // src/types/Sampler.ts
224
+ var NovelAISampler = /* @__PURE__ */ ((NovelAISampler2) => {
225
+ NovelAISampler2["Euler"] = "k_euler";
226
+ NovelAISampler2["EulerAncestral"] = "k_euler_ancestral";
227
+ NovelAISampler2["DPM_2M"] = "k_dpmpp_2m";
228
+ NovelAISampler2["DPM_2S_Ancestral"] = "k_dpmpp_2s_ancestral";
229
+ NovelAISampler2["DPM_SDE"] = "k_dpmpp_sde";
230
+ NovelAISampler2["DDIM"] = "ddim_v3";
231
+ return NovelAISampler2;
232
+ })(NovelAISampler || {});
233
+ var UCPreset = /* @__PURE__ */ ((UCPreset2) => {
234
+ UCPreset2[UCPreset2["Heavy"] = 0] = "Heavy";
235
+ UCPreset2[UCPreset2["Light"] = 1] = "Light";
236
+ UCPreset2[UCPreset2["None"] = 2] = "None";
237
+ return UCPreset2;
238
+ })(UCPreset || {});
239
+ var NoiseSchedule = /* @__PURE__ */ ((NoiseSchedule2) => {
240
+ NoiseSchedule2["Native"] = "native";
241
+ NoiseSchedule2["Karras"] = "karras";
242
+ NoiseSchedule2["Exponential"] = "exponential";
243
+ NoiseSchedule2["Polyexponential"] = "polyexponential";
244
+ return NoiseSchedule2;
245
+ })(NoiseSchedule || {});
246
+
247
+ // src/utils/Validators.ts
248
+ var ValidationError = class extends Error {
249
+ constructor(message) {
250
+ super(message);
251
+ this.name = "ValidationError";
252
+ }
253
+ };
254
+ function validateResolution(width, height) {
255
+ if (!Number.isInteger(width) || !Number.isInteger(height)) {
256
+ throw new ValidationError("Width and height must be integers");
257
+ }
258
+ if (width <= 0 || height <= 0) {
259
+ throw new ValidationError("Width and height must be positive");
260
+ }
261
+ if (width % 64 !== 0) {
262
+ throw new ValidationError(`Width must be a multiple of 64, got ${width}`);
263
+ }
264
+ if (height % 64 !== 0) {
265
+ throw new ValidationError(`Height must be a multiple of 64, got ${height}`);
266
+ }
267
+ }
268
+ function validateSteps(steps) {
269
+ if (!Number.isInteger(steps)) {
270
+ throw new ValidationError("Steps must be an integer");
271
+ }
272
+ if (steps < 1 || steps > 50) {
273
+ throw new ValidationError(`Steps must be between 1 and 50, got ${steps}`);
274
+ }
275
+ }
276
+ function validateScale(scale) {
277
+ if (typeof scale !== "number" || isNaN(scale)) {
278
+ throw new ValidationError("Scale must be a number");
279
+ }
280
+ if (scale < 0 || scale > 10) {
281
+ throw new ValidationError(`Scale must be between 0 and 10, got ${scale}`);
282
+ }
283
+ }
284
+ function validateSeed(seed) {
285
+ if (!Number.isInteger(seed)) {
286
+ throw new ValidationError("Seed must be an integer");
287
+ }
288
+ if (seed < 0 || seed > 4294967295) {
289
+ throw new ValidationError(`Seed must be between 0 and 2^32-1, got ${seed}`);
290
+ }
291
+ }
292
+
293
+ // src/builder/ImageRequestBuilder.ts
294
+ var ImageRequestBuilder = class {
295
+ constructor(client) {
296
+ // Core parameters with sensible defaults
297
+ this.model = "nai-diffusion-4-5-full" /* V45_Full */;
298
+ this.width = 832;
299
+ this.height = 1216;
300
+ this.prompt = "";
301
+ this.characterPrompts = [];
302
+ this.negativePrompt = "";
303
+ this.seed = Math.floor(Math.random() * 4294967295);
304
+ this.steps = 23;
305
+ this.scale = 5;
306
+ this.sampler = "k_euler_ancestral" /* EulerAncestral */;
307
+ this.ucPreset = 0 /* Heavy */;
308
+ this.qualityToggle = true;
309
+ this.smea = false;
310
+ this.smeaDyn = false;
311
+ this.noiseSchedule = "karras" /* Karras */;
312
+ this.client = client;
313
+ }
314
+ /**
315
+ * Set the model to use for generation
316
+ */
317
+ setModel(model) {
318
+ this.model = model;
319
+ return this;
320
+ }
321
+ /**
322
+ * Set the output image dimensions
323
+ * @throws ValidationError if dimensions are not multiples of 64
324
+ */
325
+ setSize(width, height) {
326
+ validateResolution(width, height);
327
+ this.width = width;
328
+ this.height = height;
329
+ return this;
330
+ }
331
+ /**
332
+ * Set the main prompt (base scene description)
333
+ * For multi-character prompts, use the array overload
334
+ */
335
+ setPrompt(base, characters) {
336
+ this.prompt = base;
337
+ if (characters) {
338
+ this.characterPrompts = characters;
339
+ }
340
+ return this;
341
+ }
342
+ /**
343
+ * Add character prompts for multi-character scenes
344
+ */
345
+ addCharacter(characterPrompt) {
346
+ this.characterPrompts.push(characterPrompt);
347
+ return this;
348
+ }
349
+ /**
350
+ * Set the negative prompt (what to avoid in generation)
351
+ */
352
+ setNegativePrompt(negative) {
353
+ this.negativePrompt = negative;
354
+ return this;
355
+ }
356
+ /**
357
+ * Set the random seed for reproducible generations
358
+ */
359
+ setSeed(seed) {
360
+ validateSeed(seed);
361
+ this.seed = seed;
362
+ return this;
363
+ }
364
+ /**
365
+ * Set the number of sampling steps
366
+ * @throws ValidationError if steps are out of range (1-50)
367
+ */
368
+ setSteps(steps) {
369
+ validateSteps(steps);
370
+ this.steps = steps;
371
+ return this;
372
+ }
373
+ /**
374
+ * Set the guidance scale (CFG)
375
+ * @throws ValidationError if scale is out of range (0-10)
376
+ */
377
+ setScale(scale) {
378
+ validateScale(scale);
379
+ this.scale = scale;
380
+ return this;
381
+ }
382
+ /**
383
+ * Set the sampling algorithm
384
+ */
385
+ setSampler(sampler) {
386
+ this.sampler = sampler;
387
+ return this;
388
+ }
389
+ /**
390
+ * Set the UC preset (predefined negative prompt configuration)
391
+ */
392
+ setUCPreset(preset) {
393
+ this.ucPreset = preset;
394
+ return this;
395
+ }
396
+ /**
397
+ * Toggle automatic quality tags insertion
398
+ */
399
+ setQualityToggle(enabled) {
400
+ this.qualityToggle = enabled;
401
+ return this;
402
+ }
403
+ /**
404
+ * Enable SMEA (Sinusoidal Multipass Euler Ancestral)
405
+ * @param dynamic - If true, enables dynamic SMEA variant
406
+ */
407
+ enableSMEA(dynamic = false) {
408
+ this.smea = true;
409
+ this.smeaDyn = dynamic;
410
+ return this;
411
+ }
412
+ /**
413
+ * Disable SMEA
414
+ */
415
+ disableSMEA() {
416
+ this.smea = false;
417
+ this.smeaDyn = false;
418
+ return this;
419
+ }
420
+ /**
421
+ * Set the noise schedule algorithm
422
+ */
423
+ setNoiseSchedule(schedule) {
424
+ this.noiseSchedule = schedule;
425
+ return this;
426
+ }
427
+ /**
428
+ * Build the V4 condition input structure for prompts
429
+ */
430
+ buildV4ConditionInput(base, characters) {
431
+ return {
432
+ caption: {
433
+ base_caption: base,
434
+ char_captions: characters
435
+ },
436
+ use_coords: false,
437
+ use_order: true,
438
+ legacy_uc: false
439
+ };
440
+ }
441
+ /**
442
+ * Build the formatted input string for the API
443
+ * Combines base prompt with character prompts using pipe delimiter
444
+ */
445
+ buildInputString() {
446
+ if (this.characterPrompts.length === 0) {
447
+ return this.prompt;
448
+ }
449
+ return [this.prompt, ...this.characterPrompts].join(" | ");
450
+ }
451
+ /**
452
+ * Build the complete API payload
453
+ */
454
+ buildPayload() {
455
+ const parameters = {
456
+ width: this.width,
457
+ height: this.height,
458
+ scale: this.scale,
459
+ sampler: this.sampler,
460
+ steps: this.steps,
461
+ n_samples: 1,
462
+ seed: this.seed,
463
+ negative_prompt: this.negativePrompt,
464
+ // V4-specific structured prompts
465
+ v4_prompt: this.buildV4ConditionInput(this.prompt, this.characterPrompts),
466
+ v4_negative_prompt: this.buildV4ConditionInput(this.negativePrompt, []),
467
+ // Quality and preset controls (camelCase - API requirement)
468
+ qualityToggle: this.qualityToggle,
469
+ ucPreset: this.ucPreset,
470
+ // Technical flags (snake_case - API requirement)
471
+ params_version: 3,
472
+ noise_schedule: this.noiseSchedule,
473
+ sm: this.smea,
474
+ sm_dyn: this.smeaDyn,
475
+ prefer_brownian: true,
476
+ deliberate_euler_ancestral_bug: true,
477
+ legacy: false,
478
+ legacy_v3_extend: false
479
+ };
480
+ return {
481
+ input: this.buildInputString(),
482
+ model: this.model,
483
+ action: "generate",
484
+ parameters
485
+ };
486
+ }
487
+ /**
488
+ * Execute the image generation request
489
+ */
490
+ async generate() {
491
+ const payload = this.buildPayload();
492
+ return this.client._execute(payload);
493
+ }
494
+ };
495
+
496
+ // src/client/NovelAIClient.ts
497
+ var NovelAIClient = class {
498
+ /**
499
+ * Create a new NovelAI client
500
+ * @param config - Client configuration including token and optional base URL
501
+ */
502
+ constructor(config) {
503
+ if (!config.token) {
504
+ throw new Error("NovelAI API token is required");
505
+ }
506
+ this.apiClient = new APIClient({
507
+ token: config.token,
508
+ baseUrl: config.baseUrl,
509
+ timeout: config.timeout
510
+ });
511
+ }
512
+ /**
513
+ * Start building an image generation request
514
+ * @returns A new ImageRequestBuilder instance
515
+ */
516
+ image() {
517
+ return new ImageRequestBuilder(this);
518
+ }
519
+ /**
520
+ * Execute a generation request (called internally by the builder)
521
+ * @internal
522
+ */
523
+ async _execute(payload) {
524
+ return this.apiClient.generateImage(payload);
525
+ }
526
+ };
527
+
528
+ export { ImageRequestBuilder, NoiseSchedule, NovelAIAuthError, NovelAIClient, NovelAIError, NovelAIModel, NovelAINetworkError, NovelAIPaymentError, NovelAIRateLimitError, NovelAISampler, NovelAIServerError, NovelAIValidationError, UCPreset, toBase64, toDataURL, validateResolution, validateScale, validateSeed, validateSteps };
529
+ //# sourceMappingURL=index.js.map
530
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors/index.ts","../src/utils/ZipParser.ts","../src/network/APIClient.ts","../src/types/Model.ts","../src/types/Sampler.ts","../src/utils/Validators.ts","../src/builder/ImageRequestBuilder.ts","../src/client/NovelAIClient.ts"],"names":["NovelAIModel","NovelAISampler","UCPreset","NoiseSchedule"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Base error class for all NovelAI SDK errors\n */\nexport class NovelAIError extends Error {\n public readonly statusCode?: number;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = 'NovelAIError';\n this.statusCode = statusCode;\n }\n}\n\n/**\n * Authentication error (401/403)\n * Thrown when the API token is invalid or expired\n */\nexport class NovelAIAuthError extends NovelAIError {\n constructor(message = 'Authentication failed. Check your Persistent API Token (pst-...).') {\n super(message, 401);\n this.name = 'NovelAIAuthError';\n }\n}\n\n/**\n * Payment required error (402)\n * Thrown when the user has insufficient Anlas credits\n */\nexport class NovelAIPaymentError extends NovelAIError {\n constructor(message = 'Insufficient Anlas credits for this generation.') {\n super(message, 402);\n this.name = 'NovelAIPaymentError';\n }\n}\n\n/**\n * Validation error (400)\n * Thrown when the API rejects the payload due to schema issues\n */\nexport class NovelAIValidationError extends NovelAIError {\n public readonly details?: string;\n\n constructor(message: string, details?: string) {\n super(message, 400);\n this.name = 'NovelAIValidationError';\n this.details = details;\n }\n}\n\n/**\n * Network error\n * Thrown on timeouts, connection failures, or other network issues\n */\nexport class NovelAINetworkError extends NovelAIError {\n public readonly cause?: Error;\n\n constructor(message: string, cause?: Error) {\n super(message);\n this.name = 'NovelAINetworkError';\n this.cause = cause;\n }\n}\n\n/**\n * Rate limit error (429)\n * Thrown when too many requests are made\n */\nexport class NovelAIRateLimitError extends NovelAIError {\n public readonly retryAfter?: number;\n\n constructor(message = 'Rate limit exceeded. Please wait before retrying.', retryAfter?: number) {\n super(message, 429);\n this.name = 'NovelAIRateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Server error (500+)\n * Thrown when the NovelAI server encounters an internal error\n */\nexport class NovelAIServerError extends NovelAIError {\n public readonly correlationId?: string;\n\n constructor(message = 'NovelAI server error. This may be due to payload schema issues.', correlationId?: string) {\n super(message, 500);\n this.name = 'NovelAIServerError';\n this.correlationId = correlationId;\n }\n}\n","/**\n * Isomorphic ZIP parser that works in both Node.js and browser environments\n * Extracts PNG files from NovelAI's application/x-zip-compressed response\n */\n\n/**\n * Detects if we're running in a Node.js environment\n */\nfunction isNodeEnvironment(): boolean {\n return (\n typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null\n );\n}\n\n/**\n * Extract PNG files from a ZIP archive buffer\n * @param data - The raw ZIP archive data\n * @returns Array of extracted PNG file contents\n */\nexport async function extractImagesFromZip(\n data: ArrayBuffer | Uint8Array\n): Promise<Uint8Array[]> {\n const buffer = data instanceof ArrayBuffer ? new Uint8Array(data) : data;\n\n if (isNodeEnvironment()) {\n return extractWithAdmZip(buffer);\n } else {\n return extractWithFflate(buffer);\n }\n}\n\n/**\n * Node.js extraction using adm-zip\n */\nasync function extractWithAdmZip(buffer: Uint8Array): Promise<Uint8Array[]> {\n // Dynamic import for Node.js environment\n const AdmZip = (await import('adm-zip')).default;\n const zip = new AdmZip(Buffer.from(buffer));\n const entries = zip.getEntries();\n const images: Uint8Array[] = [];\n\n for (const entry of entries) {\n if (entry.entryName.endsWith('.png') && !entry.isDirectory) {\n const data = entry.getData();\n images.push(new Uint8Array(data));\n }\n }\n\n return images;\n}\n\n/**\n * Browser extraction using fflate\n */\nasync function extractWithFflate(buffer: Uint8Array): Promise<Uint8Array[]> {\n // Dynamic import for browser environment\n const { unzipSync } = await import('fflate');\n const unzipped = unzipSync(buffer);\n const images: Uint8Array[] = [];\n\n for (const [filename, data] of Object.entries(unzipped)) {\n if (filename.endsWith('.png')) {\n images.push(data);\n }\n }\n\n return images;\n}\n\n/**\n * Convert Uint8Array to Base64 string (useful for browser display)\n */\nexport function toBase64(data: Uint8Array): string {\n if (isNodeEnvironment()) {\n return Buffer.from(data).toString('base64');\n } else {\n // Browser environment\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]);\n }\n return btoa(binary);\n }\n}\n\n/**\n * Convert Uint8Array to data URL for direct use in img src\n */\nexport function toDataURL(data: Uint8Array, mimeType = 'image/png'): string {\n return `data:${mimeType};base64,${toBase64(data)}`;\n}\n","import type { GenerateImagePayload, ImageResponse, APIErrorResponse } from '../types';\nimport {\n NovelAIError,\n NovelAIAuthError,\n NovelAIPaymentError,\n NovelAIValidationError,\n NovelAINetworkError,\n NovelAIRateLimitError,\n NovelAIServerError,\n} from '../errors';\nimport { extractImagesFromZip } from '../utils/ZipParser';\n\nconst DEFAULT_BASE_URL = 'https://image.novelai.net';\nconst DEFAULT_TIMEOUT = 60000;\nconst GENERATE_ENDPOINT = '/ai/generate-image';\n\nexport interface APIClientConfig {\n token: string;\n baseUrl?: string;\n timeout?: number;\n}\n\n/**\n * Low-level HTTP client for NovelAI API\n * Handles authentication, binary response parsing, and error mapping\n */\nexport class APIClient {\n private readonly token: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: APIClientConfig) {\n this.token = config.token;\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT;\n }\n\n /**\n * Generate an image using the NovelAI API\n */\n async generateImage(payload: GenerateImagePayload): Promise<ImageResponse> {\n const url = `${this.baseUrl}${GENERATE_ENDPOINT}`;\n\n // Generate correlation ID for debugging\n const correlationId = this.generateCorrelationId();\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n 'Accept': 'application/x-zip-compressed, application/json',\n 'x-correlation-id': correlationId,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n await this.handleErrorResponse(response, correlationId);\n }\n\n // Parse the ZIP response\n const arrayBuffer = await response.arrayBuffer();\n const images = await extractImagesFromZip(arrayBuffer);\n\n return {\n images,\n metadata: {\n seed: payload.parameters.seed,\n prompt: payload.input,\n model: payload.model,\n sampler: payload.parameters.sampler as string,\n steps: payload.parameters.steps,\n scale: payload.parameters.scale,\n },\n };\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof NovelAIError) {\n throw error;\n }\n\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new NovelAINetworkError(`Request timed out after ${this.timeout}ms`, error);\n }\n throw new NovelAINetworkError(`Network error: ${error.message}`, error);\n }\n\n throw new NovelAINetworkError('An unknown error occurred');\n }\n }\n\n /**\n * Handle non-2xx responses and throw appropriate errors\n */\n private async handleErrorResponse(response: Response, correlationId: string): Promise<never> {\n let errorBody: APIErrorResponse | null = null;\n\n try {\n errorBody = await response.json() as APIErrorResponse;\n } catch {\n // Response may not be JSON\n }\n\n const message = errorBody?.message ?? errorBody?.error ?? response.statusText;\n\n switch (response.status) {\n case 400:\n throw new NovelAIValidationError(\n `Bad request: ${message}`,\n JSON.stringify(errorBody)\n );\n case 401:\n case 403:\n throw new NovelAIAuthError(message);\n case 402:\n throw new NovelAIPaymentError(message);\n case 429: {\n const retryAfter = response.headers.get('retry-after');\n throw new NovelAIRateLimitError(message, retryAfter ? parseInt(retryAfter) : undefined);\n }\n case 500:\n case 502:\n case 503:\n case 504:\n throw new NovelAIServerError(\n `Server error (${response.status}): ${message}. This may be caused by payload schema issues.`,\n correlationId\n );\n default:\n throw new NovelAIError(\n `HTTP ${response.status}: ${message}`,\n response.status\n );\n }\n }\n\n /**\n * Generate a 6-character alphanumeric correlation ID for debugging\n */\n private generateCorrelationId(): string {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n let result = '';\n for (let i = 0; i < 6; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n }\n}\n","/**\n * Supported NovelAI image generation models\n */\nexport enum NovelAIModel {\n /** V4.5 Full - Latest model with T5 encoding and multi-character support */\n V45_Full = \"nai-diffusion-4-5-full\",\n /** V4 Curated - More conservative aesthetic choices */\n V4_Curated = \"nai-diffusion-4-curated\",\n /** V4 Inpainting - For image editing and inpainting tasks */\n Inpainting = \"nai-diffusion-4-inpainting\",\n}\n","/**\n * Supported sampling algorithms for image generation\n */\nexport enum NovelAISampler {\n /** Standard Euler sampler */\n Euler = \"k_euler\",\n /** Euler Ancestral - recommended for anime-style generations */\n EulerAncestral = \"k_euler_ancestral\",\n /** DPM++ 2M - stable and aesthetic */\n DPM_2M = \"k_dpmpp_2m\",\n /** DPM++ 2S Ancestral */\n DPM_2S_Ancestral = \"k_dpmpp_2s_ancestral\",\n /** DPM++ SDE */\n DPM_SDE = \"k_dpmpp_sde\",\n /** DDIM V3 */\n DDIM = \"ddim_v3\",\n}\n\n/**\n * Preset negative prompt configurations\n */\nexport enum UCPreset {\n /** Heavy - Low Quality + Bad Anatomy */\n Heavy = 0,\n /** Light - Low Quality only */\n Light = 1,\n /** None - Use custom negative prompt only */\n None = 2,\n}\n\n/**\n * Noise schedule algorithms\n */\nexport enum NoiseSchedule {\n Native = \"native\",\n Karras = \"karras\",\n Exponential = \"exponential\",\n Polyexponential = \"polyexponential\",\n}\n","/**\n * Validation error for invalid SDK parameters\n */\nexport class ValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Validates that image dimensions are multiples of 64\n * @throws ValidationError if dimensions are invalid\n */\nexport function validateResolution(width: number, height: number): void {\n if (!Number.isInteger(width) || !Number.isInteger(height)) {\n throw new ValidationError('Width and height must be integers');\n }\n if (width <= 0 || height <= 0) {\n throw new ValidationError('Width and height must be positive');\n }\n if (width % 64 !== 0) {\n throw new ValidationError(`Width must be a multiple of 64, got ${width}`);\n }\n if (height % 64 !== 0) {\n throw new ValidationError(`Height must be a multiple of 64, got ${height}`);\n }\n}\n\n/**\n * Validates that step count is within acceptable range\n * @throws ValidationError if steps are out of range\n */\nexport function validateSteps(steps: number): void {\n if (!Number.isInteger(steps)) {\n throw new ValidationError('Steps must be an integer');\n }\n if (steps < 1 || steps > 50) {\n throw new ValidationError(`Steps must be between 1 and 50, got ${steps}`);\n }\n}\n\n/**\n * Validates that scale (CFG) is within acceptable range\n * @throws ValidationError if scale is out of range\n */\nexport function validateScale(scale: number): void {\n if (typeof scale !== 'number' || isNaN(scale)) {\n throw new ValidationError('Scale must be a number');\n }\n if (scale < 0 || scale > 10) {\n throw new ValidationError(`Scale must be between 0 and 10, got ${scale}`);\n }\n}\n\n/**\n * Validates that seed is a valid unsigned 32-bit integer\n */\nexport function validateSeed(seed: number): void {\n if (!Number.isInteger(seed)) {\n throw new ValidationError('Seed must be an integer');\n }\n if (seed < 0 || seed > 4294967295) {\n throw new ValidationError(`Seed must be between 0 and 2^32-1, got ${seed}`);\n }\n}\n","import type { NovelAIClient } from '../client/NovelAIClient';\nimport type {\n GenerateImagePayload,\n NovelAIParameters,\n V4ConditionInput,\n ImageResponse,\n} from '../types';\nimport { NovelAIModel, NovelAISampler, UCPreset, NoiseSchedule } from '../types';\nimport { validateResolution, validateSteps, validateScale, validateSeed } from '../utils/Validators';\n\n/**\n * Fluent builder for constructing NovelAI image generation requests\n * Provides method chaining and compile-time type safety\n */\nexport class ImageRequestBuilder {\n private client: NovelAIClient;\n\n // Core parameters with sensible defaults\n private model: NovelAIModel = NovelAIModel.V45_Full;\n private width = 832;\n private height = 1216;\n private prompt = '';\n private characterPrompts: string[] = [];\n private negativePrompt = '';\n private seed: number = Math.floor(Math.random() * 4294967295);\n private steps = 23;\n private scale = 5.0;\n private sampler: NovelAISampler = NovelAISampler.EulerAncestral;\n private ucPreset: UCPreset = UCPreset.Heavy;\n private qualityToggle = true;\n private smea = false;\n private smeaDyn = false;\n private noiseSchedule: NoiseSchedule = NoiseSchedule.Karras;\n\n constructor(client: NovelAIClient) {\n this.client = client;\n }\n\n /**\n * Set the model to use for generation\n */\n setModel(model: NovelAIModel): this {\n this.model = model;\n return this;\n }\n\n /**\n * Set the output image dimensions\n * @throws ValidationError if dimensions are not multiples of 64\n */\n setSize(width: number, height: number): this {\n validateResolution(width, height);\n this.width = width;\n this.height = height;\n return this;\n }\n\n /**\n * Set the main prompt (base scene description)\n * For multi-character prompts, use the array overload\n */\n setPrompt(base: string, characters?: string[]): this {\n this.prompt = base;\n if (characters) {\n this.characterPrompts = characters;\n }\n return this;\n }\n\n /**\n * Add character prompts for multi-character scenes\n */\n addCharacter(characterPrompt: string): this {\n this.characterPrompts.push(characterPrompt);\n return this;\n }\n\n /**\n * Set the negative prompt (what to avoid in generation)\n */\n setNegativePrompt(negative: string): this {\n this.negativePrompt = negative;\n return this;\n }\n\n /**\n * Set the random seed for reproducible generations\n */\n setSeed(seed: number): this {\n validateSeed(seed);\n this.seed = seed;\n return this;\n }\n\n /**\n * Set the number of sampling steps\n * @throws ValidationError if steps are out of range (1-50)\n */\n setSteps(steps: number): this {\n validateSteps(steps);\n this.steps = steps;\n return this;\n }\n\n /**\n * Set the guidance scale (CFG)\n * @throws ValidationError if scale is out of range (0-10)\n */\n setScale(scale: number): this {\n validateScale(scale);\n this.scale = scale;\n return this;\n }\n\n /**\n * Set the sampling algorithm\n */\n setSampler(sampler: NovelAISampler): this {\n this.sampler = sampler;\n return this;\n }\n\n /**\n * Set the UC preset (predefined negative prompt configuration)\n */\n setUCPreset(preset: UCPreset): this {\n this.ucPreset = preset;\n return this;\n }\n\n /**\n * Toggle automatic quality tags insertion\n */\n setQualityToggle(enabled: boolean): this {\n this.qualityToggle = enabled;\n return this;\n }\n\n /**\n * Enable SMEA (Sinusoidal Multipass Euler Ancestral)\n * @param dynamic - If true, enables dynamic SMEA variant\n */\n enableSMEA(dynamic = false): this {\n this.smea = true;\n this.smeaDyn = dynamic;\n return this;\n }\n\n /**\n * Disable SMEA\n */\n disableSMEA(): this {\n this.smea = false;\n this.smeaDyn = false;\n return this;\n }\n\n /**\n * Set the noise schedule algorithm\n */\n setNoiseSchedule(schedule: NoiseSchedule): this {\n this.noiseSchedule = schedule;\n return this;\n }\n\n /**\n * Build the V4 condition input structure for prompts\n */\n private buildV4ConditionInput(base: string, characters: string[]): V4ConditionInput {\n return {\n caption: {\n base_caption: base,\n char_captions: characters,\n },\n use_coords: false,\n use_order: true,\n legacy_uc: false,\n };\n }\n\n /**\n * Build the formatted input string for the API\n * Combines base prompt with character prompts using pipe delimiter\n */\n private buildInputString(): string {\n if (this.characterPrompts.length === 0) {\n return this.prompt;\n }\n return [this.prompt, ...this.characterPrompts].join(' | ');\n }\n\n /**\n * Build the complete API payload\n */\n buildPayload(): GenerateImagePayload {\n const parameters: NovelAIParameters = {\n width: this.width,\n height: this.height,\n scale: this.scale,\n sampler: this.sampler,\n steps: this.steps,\n n_samples: 1,\n seed: this.seed,\n negative_prompt: this.negativePrompt,\n\n // V4-specific structured prompts\n v4_prompt: this.buildV4ConditionInput(this.prompt, this.characterPrompts),\n v4_negative_prompt: this.buildV4ConditionInput(this.negativePrompt, []),\n\n // Quality and preset controls (camelCase - API requirement)\n qualityToggle: this.qualityToggle,\n ucPreset: this.ucPreset,\n\n // Technical flags (snake_case - API requirement)\n params_version: 3,\n noise_schedule: this.noiseSchedule,\n sm: this.smea,\n sm_dyn: this.smeaDyn,\n prefer_brownian: true,\n deliberate_euler_ancestral_bug: true,\n legacy: false,\n legacy_v3_extend: false,\n };\n\n return {\n input: this.buildInputString(),\n model: this.model,\n action: 'generate',\n parameters,\n };\n }\n\n /**\n * Execute the image generation request\n */\n async generate(): Promise<ImageResponse> {\n const payload = this.buildPayload();\n return this.client._execute(payload);\n }\n}\n","import type { ClientConfig, GenerateImagePayload, ImageResponse } from '../types';\nimport { APIClient } from '../network/APIClient';\nimport { ImageRequestBuilder } from '../builder/ImageRequestBuilder';\n\n/**\n * Main entry point for the NovelAI Image SDK\n * \n * @example\n * ```typescript\n * const client = new NovelAIClient({ token: process.env.NAI_TOKEN });\n * \n * const result = await client.image()\n * .setPrompt('1girl, cyberpunk, neon lights')\n * .setSize(832, 1216)\n * .generate();\n * \n * fs.writeFileSync('output.png', result.images[0]);\n * ```\n */\nexport class NovelAIClient {\n private readonly apiClient: APIClient;\n\n /**\n * Create a new NovelAI client\n * @param config - Client configuration including token and optional base URL\n */\n constructor(config: ClientConfig) {\n if (!config.token) {\n throw new Error('NovelAI API token is required');\n }\n\n this.apiClient = new APIClient({\n token: config.token,\n baseUrl: config.baseUrl,\n timeout: config.timeout,\n });\n }\n\n /**\n * Start building an image generation request\n * @returns A new ImageRequestBuilder instance\n */\n image(): ImageRequestBuilder {\n return new ImageRequestBuilder(this);\n }\n\n /**\n * Execute a generation request (called internally by the builder)\n * @internal\n */\n async _execute(payload: GenerateImagePayload): Promise<ImageResponse> {\n return this.apiClient.generateImage(payload);\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "novelai-image-sdk",
3
+ "version": "1.0.0",
4
+ "description": "Type-safe TypeScript SDK for NovelAI V4.5 image generation API",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "dev": "tsup --watch",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "test:coverage": "vitest run --coverage",
30
+ "test:live": "vitest run tests/integration.test.ts",
31
+ "lint": "tsc --noEmit",
32
+ "prepublishOnly": "npm run build"
33
+ },
34
+ "keywords": [
35
+ "novelai",
36
+ "image-generation",
37
+ "diffusion",
38
+ "ai",
39
+ "typescript",
40
+ "sdk"
41
+ ],
42
+ "author": "",
43
+ "license": "MIT",
44
+ "dependencies": {
45
+ "adm-zip": "^0.5.10",
46
+ "fflate": "^0.8.2"
47
+ },
48
+ "devDependencies": {
49
+ "@types/adm-zip": "^0.5.5",
50
+ "@types/node": "^20.11.0",
51
+ "tsup": "^8.0.1",
52
+ "typescript": "^5.3.3",
53
+ "vitest": "^1.2.0"
54
+ },
55
+ "engines": {
56
+ "node": ">=18.0.0"
57
+ }
58
+ }