@qafka/react-native 2.4.2 → 2.5.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/CHANGELOG.md +6 -0
- package/README.md +72 -0
- package/dist/QafkaSDK.d.ts +5 -1
- package/dist/QafkaSDK.js +15 -0
- package/dist/services/BackendService.d.ts +21 -2
- package/dist/services/BackendService.js +47 -3
- package/dist/types/image.d.ts +23 -0
- package/dist/types/image.js +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.5.0] — 2026-07-19
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Image generation: a new `generateImage()` method creates an image from a text description, or reframes one or more reference images to a target aspect ratio or exact pixel size without stretching or distortion. Results can be returned as base64, a signed time-limited URL, or both, and multiple variants can be requested in a single call. Higher-quality `pro` output is available when it is enabled on your plan. See the "Image Generation" section of the README for the full API, response shape, and error handling.
|
|
15
|
+
|
|
10
16
|
## [2.4.2] — 2026-06-22
|
|
11
17
|
|
|
12
18
|
### Fixed
|
package/README.md
CHANGED
|
@@ -87,6 +87,78 @@ Pass `projectId` when your app has more than one registered project:
|
|
|
87
87
|
<Qafka projectId="proj_abc123" />
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
## Image Generation
|
|
91
|
+
|
|
92
|
+
Generate images from text descriptions or reframe reference images to target aspect ratios and sizes without distortion. The SDK must be initialized before calling this method.
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { getSDK } from '@qafka/react-native';
|
|
96
|
+
|
|
97
|
+
const result = await getSDK().generateImage({
|
|
98
|
+
description: 'product photo reframed to 16:9 landscape',
|
|
99
|
+
referenceImage: 'https://example.com/photo.jpg',
|
|
100
|
+
aspectRatio: '16:9',
|
|
101
|
+
quality: 'standard',
|
|
102
|
+
n: 1,
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Parameters
|
|
107
|
+
|
|
108
|
+
| Param | Type | Required | Notes |
|
|
109
|
+
|-------|------|----------|-------|
|
|
110
|
+
| `description` | string | Yes | Text description of the desired image or transformation. |
|
|
111
|
+
| `referenceImage` | string \| string[] | No | Base64 data-URI, URL, or array of either. Used to guide or reframe the output. |
|
|
112
|
+
| `aspectRatio` | string | No | Free-form ratio, e.g. `"16:9"`, `"7:3"`, `"1:1"`. Ignored if `size` is set. |
|
|
113
|
+
| `size` | `{ width: number; height: number }` | No | Exact pixel dimensions. Overrides `aspectRatio`. |
|
|
114
|
+
| `quality` | `'standard' \| 'pro'` | No | Default: `'standard'`. `'pro'` requires image generation pro to be enabled on your plan. |
|
|
115
|
+
| `responseType` | `'base64' \| 'url' \| 'both'` | No | Default: `'base64'`. `'url'` returns a signed, time-limited link; `'both'` returns both. |
|
|
116
|
+
| `n` | number | No | Number of variants to generate. Default: `1`. When `n > 1`, result is an array. |
|
|
117
|
+
|
|
118
|
+
### Result
|
|
119
|
+
|
|
120
|
+
Each generated image is an object with:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
{
|
|
124
|
+
base64?: string; // Returned when responseType includes 'base64'
|
|
125
|
+
url?: string; // Signed link, time-limited. Returned when responseType includes 'url'
|
|
126
|
+
expiresAt?: string; // ISO 8601 timestamp. Present when url is returned
|
|
127
|
+
mimeType: string; // e.g. 'image/png'
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
When `n > 1`, the result is an array of such objects.
|
|
132
|
+
|
|
133
|
+
### Example
|
|
134
|
+
|
|
135
|
+
Reframe an uploaded product photo to a banner size and fetch the result as a base64 image:
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
try {
|
|
139
|
+
const result = await getSDK().generateImage({
|
|
140
|
+
description: 'High-quality product banner, maintaining all product details',
|
|
141
|
+
referenceImage: productPhotoBase64,
|
|
142
|
+
size: { width: 1200, height: 300 },
|
|
143
|
+
responseType: 'base64',
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
setGeneratedImage(result.base64);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
if (err.code === 'not_entitled') {
|
|
149
|
+
showAlert('Image generation is not enabled on your plan.');
|
|
150
|
+
} else if (err.code === 'rate_limited') {
|
|
151
|
+
showAlert('Too many requests. Please try again later.');
|
|
152
|
+
} else if (err.code === 'content_filtered') {
|
|
153
|
+
showAlert('The description or reference image was filtered for policy reasons.');
|
|
154
|
+
} else if (err.code === 'invalid_reference') {
|
|
155
|
+
showAlert('The reference image could not be processed.');
|
|
156
|
+
} else {
|
|
157
|
+
showAlert('Image generation failed. Please try again.');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
90
162
|
## Documentation
|
|
91
163
|
|
|
92
164
|
Full guides, API reference, theming, voice mode, tools, and advanced configuration:
|
package/dist/QafkaSDK.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SDKConfig, SDKStatusType, ChatMessage, ChatResponse } from './types';
|
|
1
|
+
import { SDKConfig, SDKStatusType, ChatMessage, ChatResponse, GenerateImageParams, GenerateImageResult } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* QafkaSDK - Main SDK class
|
|
4
4
|
* Provides AI-powered conversational interface for React Native apps
|
|
@@ -131,6 +131,10 @@ export declare class QafkaSDK {
|
|
|
131
131
|
type: string;
|
|
132
132
|
}, conversationId?: string): Promise<any>;
|
|
133
133
|
/**
|
|
134
|
+
* Generate an image based on a description
|
|
135
|
+
*/
|
|
136
|
+
generateImage(params: GenerateImageParams): Promise<GenerateImageResult>;
|
|
137
|
+
/**
|
|
134
138
|
* Get project theme configuration
|
|
135
139
|
*/
|
|
136
140
|
getProjectTheme(): Promise<any>;
|
package/dist/QafkaSDK.js
CHANGED
|
@@ -383,6 +383,21 @@ class QafkaSDK {
|
|
|
383
383
|
return this.backendService.uploadFile(toolId, file, conversationId);
|
|
384
384
|
}
|
|
385
385
|
/**
|
|
386
|
+
* Generate an image based on a description
|
|
387
|
+
*/
|
|
388
|
+
async generateImage(params) {
|
|
389
|
+
if (this.status !== 'ready') {
|
|
390
|
+
throw new Error('SDK not initialized. Call initialize() first.');
|
|
391
|
+
}
|
|
392
|
+
if (!params?.description?.trim()) {
|
|
393
|
+
throw new Error('description cannot be empty');
|
|
394
|
+
}
|
|
395
|
+
if (!this.backendService) {
|
|
396
|
+
throw new Error('Backend service not initialized');
|
|
397
|
+
}
|
|
398
|
+
return this.backendService.generateImage(params);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
386
401
|
* Get project theme configuration
|
|
387
402
|
*/
|
|
388
403
|
async getProjectTheme() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatResponse } from '../types';
|
|
1
|
+
import { ChatResponse, GenerateImageParams, GenerateImageResult } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Backend API Service using native fetch
|
|
4
4
|
*/
|
|
@@ -19,6 +19,7 @@ export declare class BackendService {
|
|
|
19
19
|
private onToolDataRequested;
|
|
20
20
|
private readonly toolDataResolvedFor;
|
|
21
21
|
private static readonly RESOLVER_TIMEOUT_MS;
|
|
22
|
+
private static readonly IMAGE_TIMEOUT_MS;
|
|
22
23
|
constructor(apiKey: string | null, apiUrl?: string, subProjectId?: string);
|
|
23
24
|
setSessionTokenGetter(getter: () => Promise<string | null>): void;
|
|
24
25
|
/**
|
|
@@ -80,7 +81,9 @@ export declare class BackendService {
|
|
|
80
81
|
*/
|
|
81
82
|
private postToolData;
|
|
82
83
|
/**
|
|
83
|
-
* Make HTTP request with timeout
|
|
84
|
+
* Make HTTP request with timeout. `timeoutMs` optionally overrides the
|
|
85
|
+
* instance default (`this.timeout`) for calls that are known to run long,
|
|
86
|
+
* e.g. image generation.
|
|
84
87
|
*/
|
|
85
88
|
private fetchWithTimeout;
|
|
86
89
|
/**
|
|
@@ -104,6 +107,22 @@ export declare class BackendService {
|
|
|
104
107
|
*/
|
|
105
108
|
getTheme(): Promise<any>;
|
|
106
109
|
/**
|
|
110
|
+
* Generate one or more images via the backend `/images` endpoint.
|
|
111
|
+
* Uses a much longer timeout than the default (image inference is slow).
|
|
112
|
+
* When `params.n` is absent or <= 1, the single result is unwrapped from
|
|
113
|
+
* the `images` array; otherwise the full array is returned.
|
|
114
|
+
*/
|
|
115
|
+
generateImage(params: GenerateImageParams): Promise<GenerateImageResult>;
|
|
116
|
+
/**
|
|
117
|
+
* Response handler for `/images`. Mirrors `handleResponse`'s ok/!ok split
|
|
118
|
+
* but maps the server's `errorData.error` onto `Error.code` (same
|
|
119
|
+
* augmentation pattern as the `DEV_KEY_INVALID` error in
|
|
120
|
+
* `sendMessageStream`) so callers can branch on stable error codes
|
|
121
|
+
* (`not_entitled`, `rate_limited`, `content_filtered`, `invalid_reference`,
|
|
122
|
+
* `generation_failed`, ...) instead of parsing message strings.
|
|
123
|
+
*/
|
|
124
|
+
private handleImageResponse;
|
|
125
|
+
/**
|
|
107
126
|
* Send message with streaming response
|
|
108
127
|
* React Native compatible version using XMLHttpRequest
|
|
109
128
|
*/
|
|
@@ -32,6 +32,9 @@ class BackendService {
|
|
|
32
32
|
onToolDataRequested = null;
|
|
33
33
|
toolDataResolvedFor = new Map();
|
|
34
34
|
static RESOLVER_TIMEOUT_MS = 3000;
|
|
35
|
+
// Image generation is slow (model inference); give it a much longer
|
|
36
|
+
// budget than the default 60s used for chat.
|
|
37
|
+
static IMAGE_TIMEOUT_MS = 120_000;
|
|
35
38
|
constructor(apiKey, apiUrl, subProjectId) {
|
|
36
39
|
// If custom apiUrl provided, ensure it has /api/v1 prefix
|
|
37
40
|
if (apiUrl) {
|
|
@@ -233,11 +236,13 @@ class BackendService {
|
|
|
233
236
|
});
|
|
234
237
|
}
|
|
235
238
|
/**
|
|
236
|
-
* Make HTTP request with timeout
|
|
239
|
+
* Make HTTP request with timeout. `timeoutMs` optionally overrides the
|
|
240
|
+
* instance default (`this.timeout`) for calls that are known to run long,
|
|
241
|
+
* e.g. image generation.
|
|
237
242
|
*/
|
|
238
|
-
async fetchWithTimeout(url, options) {
|
|
243
|
+
async fetchWithTimeout(url, options, timeoutMs = this.timeout) {
|
|
239
244
|
const controller = new AbortController();
|
|
240
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
245
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
241
246
|
try {
|
|
242
247
|
const response = await this.fetchWithRefresh(url, {
|
|
243
248
|
...options,
|
|
@@ -410,6 +415,45 @@ class BackendService {
|
|
|
410
415
|
}
|
|
411
416
|
}
|
|
412
417
|
/**
|
|
418
|
+
* Generate one or more images via the backend `/images` endpoint.
|
|
419
|
+
* Uses a much longer timeout than the default (image inference is slow).
|
|
420
|
+
* When `params.n` is absent or <= 1, the single result is unwrapped from
|
|
421
|
+
* the `images` array; otherwise the full array is returned.
|
|
422
|
+
*/
|
|
423
|
+
async generateImage(params) {
|
|
424
|
+
const response = await this.fetchWithTimeout(`${this.baseURL}/images`, {
|
|
425
|
+
method: 'POST',
|
|
426
|
+
body: JSON.stringify(params),
|
|
427
|
+
}, BackendService.IMAGE_TIMEOUT_MS);
|
|
428
|
+
const data = await this.handleImageResponse(response);
|
|
429
|
+
const images = data.images ?? [];
|
|
430
|
+
return (params.n ?? 1) > 1 ? images : images[0];
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Response handler for `/images`. Mirrors `handleResponse`'s ok/!ok split
|
|
434
|
+
* but maps the server's `errorData.error` onto `Error.code` (same
|
|
435
|
+
* augmentation pattern as the `DEV_KEY_INVALID` error in
|
|
436
|
+
* `sendMessageStream`) so callers can branch on stable error codes
|
|
437
|
+
* (`not_entitled`, `rate_limited`, `content_filtered`, `invalid_reference`,
|
|
438
|
+
* `generation_failed`, ...) instead of parsing message strings.
|
|
439
|
+
*/
|
|
440
|
+
async handleImageResponse(response) {
|
|
441
|
+
if (response.ok) {
|
|
442
|
+
return response.json();
|
|
443
|
+
}
|
|
444
|
+
const errorData = await response.json().catch(() => ({}));
|
|
445
|
+
const err = new Error(errorData.message || `❌ Image generation failed (HTTP ${response.status})`);
|
|
446
|
+
err.code = errorData.error;
|
|
447
|
+
err.status = response.status;
|
|
448
|
+
// RateLimitGuard returns 429 with body { error: 'RATE_LIMIT_EXCEEDED' },
|
|
449
|
+
// but the public contract promises 'rate_limited' — normalize so callers
|
|
450
|
+
// can reliably branch on it.
|
|
451
|
+
if (response.status === 429) {
|
|
452
|
+
err.code = 'rate_limited';
|
|
453
|
+
}
|
|
454
|
+
throw err;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
413
457
|
* Send message with streaming response
|
|
414
458
|
* React Native compatible version using XMLHttpRequest
|
|
415
459
|
*/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image generation types for QafkaSDK
|
|
3
|
+
* Parameters and results for generateImage() method
|
|
4
|
+
*/
|
|
5
|
+
export interface GenerateImageParams {
|
|
6
|
+
description: string;
|
|
7
|
+
referenceImage?: string | string[];
|
|
8
|
+
aspectRatio?: string;
|
|
9
|
+
size?: {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
quality?: 'standard' | 'pro';
|
|
14
|
+
responseType?: 'base64' | 'url' | 'both';
|
|
15
|
+
n?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface GeneratedImage {
|
|
18
|
+
base64?: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
expiresAt?: string;
|
|
21
|
+
mimeType: string;
|
|
22
|
+
}
|
|
23
|
+
export type GenerateImageResult = GeneratedImage | GeneratedImage[];
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
package/package.json
CHANGED