@yetter/client 0.0.11 → 0.0.13
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 +121 -17
- package/dist/api.d.ts +16 -1
- package/dist/api.js +130 -40
- package/dist/client.d.ts +30 -3
- package/dist/client.js +412 -156
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +63 -0
- package/package.json +9 -2
- package/examples/stream.ts +0 -54
- package/examples/submit.ts +0 -80
- package/examples/subscribe.ts +0 -41
- package/src/api.ts +0 -105
- package/src/client.ts +0 -338
- package/src/index.ts +0 -3
- package/src/types.ts +0 -116
- package/tsconfig.json +0 -13
package/README.md
CHANGED
|
@@ -8,6 +8,16 @@ The Yetter JS Client provides a convenient way to interact with the Yetter API f
|
|
|
8
8
|
npm install @yetter/client
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Testing
|
|
12
|
+
|
|
13
|
+
Run the local test suite:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm test
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Tests are automatically omitted from npm releases because `package.json` uses a `files` whitelist (`dist`, `README.md`).
|
|
20
|
+
|
|
11
21
|
## Authentication
|
|
12
22
|
|
|
13
23
|
The client requires a Yetter API key for authentication. Ensure you have the `YTR_API_KEY` or `REACT_APP_YTR_API_KEY` environment variable set:
|
|
@@ -30,6 +40,44 @@ yetter.configure({
|
|
|
30
40
|
|
|
31
41
|
If `yetter.configure()` is used, it will override any API key found in environment variables for subsequent API calls. If neither environment variables are set nor `yetter.configure()` is called with an API key, an error will be thrown when attempting to make an API call.
|
|
32
42
|
|
|
43
|
+
## Instance Client (Recommended for Concurrent Apps)
|
|
44
|
+
|
|
45
|
+
For server or multi-tenant workloads, prefer creating isolated client instances instead of sharing global static state.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { YetterClient } from "@yetter/client";
|
|
49
|
+
|
|
50
|
+
const teamAClient = new YetterClient({ apiKey: process.env.TEAM_A_YTR_API_KEY! });
|
|
51
|
+
const teamBClient = new YetterClient({ apiKey: process.env.TEAM_B_YTR_API_KEY! });
|
|
52
|
+
|
|
53
|
+
const result = await teamAClient.subscribe("ytr-ai/qwen/image-edit/i2i", {
|
|
54
|
+
input: {
|
|
55
|
+
prompt: "Transform this image to watercolor painting style",
|
|
56
|
+
image_url: ["https://example.com/input.jpg"],
|
|
57
|
+
num_inference_steps: 28,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Running i2i Example Scripts
|
|
63
|
+
|
|
64
|
+
For image-to-image models (such as `ytr-ai/qwen/image-edit/i2i`), provide both a prompt and an input image.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
export YTR_API_KEY="your_api_key_here"
|
|
68
|
+
export YTR_MODEL="ytr-ai/qwen/image-edit/i2i"
|
|
69
|
+
export YTR_IMAGE_PATH="./bowow2.jpeg"
|
|
70
|
+
export YTR_PROMPT="Transform this image to watercolor painting style"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then run one of:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm run test:submit
|
|
77
|
+
npm run test:subscribe
|
|
78
|
+
npm run test:stream
|
|
79
|
+
```
|
|
80
|
+
|
|
33
81
|
## Core Functionalities
|
|
34
82
|
|
|
35
83
|
The client is available via the `yetter` object imported from `yetter-js` (or the relevant path to `client.js`/`client.ts` if used directly).
|
|
@@ -44,10 +92,11 @@ This function submits an image generation request and long-polls for the result.
|
|
|
44
92
|
|
|
45
93
|
**Features:**
|
|
46
94
|
- Submits a generation request.
|
|
47
|
-
- Polls for status updates until the job is "COMPLETED" or "
|
|
95
|
+
- Polls for status updates until the job is "COMPLETED" or "ERROR".
|
|
48
96
|
- Timeout after 30 minutes, with an attempt to cancel the job.
|
|
49
97
|
- Optional `onQueueUpdate` callback for real-time feedback on queue position and status.
|
|
50
98
|
- Optional `logs` flag to include logs in status updates.
|
|
99
|
+
- Optional `pollIntervalMs` to control status polling interval (default: 2000ms).
|
|
51
100
|
|
|
52
101
|
**Example (from `examples/subscribe.ts`):**
|
|
53
102
|
|
|
@@ -71,7 +120,7 @@ async function main() {
|
|
|
71
120
|
update.logs.map((log) => log.message).forEach(logMessage => console.log(` - ${logMessage}`));
|
|
72
121
|
} else if (update.status === "COMPLETED") {
|
|
73
122
|
console.log("Processing completed!");
|
|
74
|
-
} else if (update.status === "
|
|
123
|
+
} else if (update.status === "ERROR") {
|
|
75
124
|
console.error("Processing failed. Logs:", update.logs);
|
|
76
125
|
}
|
|
77
126
|
},
|
|
@@ -95,18 +144,18 @@ main();
|
|
|
95
144
|
This function submits an image generation request and returns an async iterable stream of events using Server-Sent Events (SSE). This allows for real-time updates on the job's progress, status, and logs.
|
|
96
145
|
|
|
97
146
|
**Features:**
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
147
|
+
|
|
148
|
+
* Initiates a request and establishes an SSE connection.
|
|
149
|
+
* Provides an `AsyncIterator` (`Symbol.asyncIterator`) to loop through status events (`StreamEvent`).
|
|
150
|
+
* A `done()` method: Returns a Promise that resolves with the final `GetResponseResponse` **after the server emits `event: done`** (successful completion), or rejects if the server emits `event: error` or the final response cannot be fetched.
|
|
151
|
+
* A `cancel()` method: Requests cancellation on the backend; the stream naturally ends when the server emits `data` (with `status: "CANCELLED"`) followed by `event: done`.
|
|
152
|
+
* A `getRequestId()` method: Returns the request ID for the stream.
|
|
103
153
|
|
|
104
154
|
**Events (`StreamEvent`):**
|
|
105
155
|
Each event pushed by the stream is an object typically including:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
- Other model-specific data.
|
|
156
|
+
|
|
157
|
+
* `status`: Current status (e.g., `"IN_QUEUE"`, `"IN_PROGRESS"`, `"COMPLETED"`, `"CANCELLED"`, `"ERROR"`).
|
|
158
|
+
* Other model-specific data.
|
|
110
159
|
|
|
111
160
|
**Example (from `examples/stream.ts`):**
|
|
112
161
|
|
|
@@ -127,9 +176,11 @@ async function main() {
|
|
|
127
176
|
streamRequestId = streamInstance.getRequestId();
|
|
128
177
|
console.log(`Stream initiated for Request ID: ${streamRequestId}`);
|
|
129
178
|
|
|
130
|
-
// Iterate over stream events
|
|
179
|
+
// Iterate over stream events (informational; termination is driven by server 'done'/'error' events)
|
|
131
180
|
for await (const event of streamInstance) {
|
|
132
|
-
console.log(
|
|
181
|
+
console.log(
|
|
182
|
+
`[STREAM EVENT - ${streamRequestId}] Status: ${event.status}, QPos: ${event.queue_position}`
|
|
183
|
+
);
|
|
133
184
|
if (event.logs && event.logs.length > 0) {
|
|
134
185
|
console.log(` Logs for ${streamRequestId}:`);
|
|
135
186
|
event.logs.forEach(log => console.log(` - ${log.message}`));
|
|
@@ -137,7 +188,7 @@ async function main() {
|
|
|
137
188
|
}
|
|
138
189
|
console.log(`Stream for ${streamRequestId} finished iterating events.`);
|
|
139
190
|
|
|
140
|
-
//
|
|
191
|
+
// Final result becomes available after server emits `event: done`
|
|
141
192
|
const result = await streamInstance.done();
|
|
142
193
|
console.log("\n--- Stream Test Final Result ---");
|
|
143
194
|
console.log("Generated Images:", result.images);
|
|
@@ -197,11 +248,11 @@ async function main() {
|
|
|
197
248
|
console.log("Request ID:", request_id);
|
|
198
249
|
|
|
199
250
|
if (request_id) {
|
|
200
|
-
console.log(`\n--- Polling for status of Request ID: ${request_id} ---`);
|
|
251
|
+
console.log(`\n--- Polling for status of Request ID: ${request_id} (10-minute timeout) ---`);
|
|
201
252
|
// Polling logic:
|
|
202
253
|
let success = false;
|
|
203
254
|
const startTime = Date.now();
|
|
204
|
-
const timeoutMilliseconds =
|
|
255
|
+
const timeoutMilliseconds = 10 * 60 * 1000; // 10 minutes
|
|
205
256
|
const pollIntervalMilliseconds = 10 * 1000; // Poll every 10 seconds
|
|
206
257
|
|
|
207
258
|
while (Date.now() - startTime < timeoutMilliseconds) {
|
|
@@ -215,7 +266,7 @@ async function main() {
|
|
|
215
266
|
console.log("Image Data:", finalResult.data.images);
|
|
216
267
|
success = true;
|
|
217
268
|
break;
|
|
218
|
-
} else if (currentStatus === "
|
|
269
|
+
} else if (currentStatus === "ERROR") {
|
|
219
270
|
console.error(`Request ${request_id} FAILED. Logs:`, statusResult.data.logs);
|
|
220
271
|
break;
|
|
221
272
|
}
|
|
@@ -231,6 +282,59 @@ async function main() {
|
|
|
231
282
|
main();
|
|
232
283
|
```
|
|
233
284
|
|
|
285
|
+
### 4. Uploading Input Images
|
|
286
|
+
|
|
287
|
+
For models that require an input image (e.g., image-to-image, style transfer), you can upload your image using `yetter.uploadFile()` (Node.js) or `yetter.uploadBlob()` (browser).
|
|
288
|
+
|
|
289
|
+
**Features:**
|
|
290
|
+
- Support for both Node.js filesystem paths and browser File/Blob objects
|
|
291
|
+
- Automatic single-part or multipart upload based on file size
|
|
292
|
+
- Progress tracking with optional callback
|
|
293
|
+
- Returns a public URL to use in generation requests
|
|
294
|
+
|
|
295
|
+
#### Node.js Example
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import { yetter } from "@yetter/client";
|
|
299
|
+
|
|
300
|
+
yetter.configure({ apiKey: process.env.YTR_API_KEY });
|
|
301
|
+
|
|
302
|
+
// Upload image
|
|
303
|
+
const uploadResult = await yetter.uploadFile("./input-image.jpg", {
|
|
304
|
+
onProgress: (percent) => console.log(`Upload: ${percent}%`),
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Use in generation
|
|
308
|
+
const result = await yetter.subscribe("ytr-ai/model/i2i", {
|
|
309
|
+
input: {
|
|
310
|
+
prompt: "Transform to anime style",
|
|
311
|
+
image_url: [uploadResult.url],
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
#### Browser Example
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// HTML: <input type="file" id="imageInput" accept="image/*">
|
|
320
|
+
|
|
321
|
+
const fileInput = document.getElementById('imageInput') as HTMLInputElement;
|
|
322
|
+
fileInput.addEventListener('change', async () => {
|
|
323
|
+
const file = fileInput.files![0];
|
|
324
|
+
|
|
325
|
+
const uploadResult = await yetter.uploadBlob(file, {
|
|
326
|
+
onProgress: (pct) => updateProgressBar(pct),
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
console.log("Uploaded:", uploadResult.url);
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Upload Options:**
|
|
334
|
+
- `onProgress?: (progress: number) => void` - Progress callback (0-100)
|
|
335
|
+
- `timeout?: number` - Timeout in milliseconds (default: 5 minutes)
|
|
336
|
+
- `filename?: string` - Custom filename (browser uploads)
|
|
337
|
+
|
|
234
338
|
## Error Handling
|
|
235
339
|
|
|
236
340
|
The client functions generally throw errors for API issues, network problems, or failed generation requests. Ensure you wrap API calls in `try...catch` blocks to handle potential errors gracefully. Specific error messages or details (like logs for failed jobs) are often included in the thrown error object or the final status response.
|
package/dist/api.d.ts
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
|
-
import { ClientOptions, GenerateRequest, GenerateResponse, GetStatusRequest, GetStatusResponse, CancelRequest, CancelResponse, GetResponseRequest, GetResponseResponse } from "./types";
|
|
1
|
+
import { ClientOptions, GenerateRequest, GenerateResponse, GetStatusRequest, GetStatusResponse, CancelRequest, CancelResponse, GetResponseRequest, GetResponseResponse, GetUploadUrlRequest, GetUploadUrlResponse, UploadCompleteRequest, UploadCompleteResponse } from "./types";
|
|
2
2
|
export declare class YetterImageClient {
|
|
3
3
|
private apiKey;
|
|
4
4
|
private endpoint;
|
|
5
5
|
constructor(options: ClientOptions);
|
|
6
6
|
getApiEndpoint(): string;
|
|
7
|
+
private getAuthHeaders;
|
|
8
|
+
private readErrorBody;
|
|
9
|
+
private requestJson;
|
|
7
10
|
generate(body: GenerateRequest): Promise<GenerateResponse>;
|
|
8
11
|
getStatus(body: GetStatusRequest): Promise<GetStatusResponse>;
|
|
9
12
|
cancel(body: CancelRequest): Promise<CancelResponse>;
|
|
10
13
|
getResponse(body: GetResponseRequest): Promise<GetResponseResponse>;
|
|
14
|
+
/**
|
|
15
|
+
* Request presigned URL(s) for file upload
|
|
16
|
+
* @param body Upload request parameters
|
|
17
|
+
* @returns Presigned URL response with mode (single/multipart)
|
|
18
|
+
*/
|
|
19
|
+
getUploadUrl(body: GetUploadUrlRequest): Promise<GetUploadUrlResponse>;
|
|
20
|
+
/**
|
|
21
|
+
* Notify server that upload is complete
|
|
22
|
+
* @param body Completion request with S3 key
|
|
23
|
+
* @returns Uploaded file metadata with public URL
|
|
24
|
+
*/
|
|
25
|
+
uploadComplete(body: UploadCompleteRequest): Promise<UploadCompleteResponse>;
|
|
11
26
|
}
|
package/dist/api.js
CHANGED
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
import fetch from "cross-fetch";
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 30000;
|
|
3
|
+
const MAX_RETRY_DELAY_MS = 5000;
|
|
4
|
+
const BASE_RETRY_DELAY_MS = 300;
|
|
5
|
+
const RETRYABLE_STATUS_CODES = new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
6
|
+
function sleep(ms) {
|
|
7
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8
|
+
}
|
|
9
|
+
function isAbortError(error) {
|
|
10
|
+
return (error === null || error === void 0 ? void 0 : error.name) === "AbortError";
|
|
11
|
+
}
|
|
12
|
+
function isLikelyNetworkError(error) {
|
|
13
|
+
if (!error) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (error instanceof TypeError) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
const message = String(error.message || "");
|
|
20
|
+
return /(ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|NetworkError|network request failed)/i.test(message);
|
|
21
|
+
}
|
|
22
|
+
function getRetryDelayMs(attempt) {
|
|
23
|
+
const backoff = Math.min(MAX_RETRY_DELAY_MS, BASE_RETRY_DELAY_MS * (2 ** attempt));
|
|
24
|
+
const jitter = Math.floor(Math.random() * 150);
|
|
25
|
+
return backoff + jitter;
|
|
26
|
+
}
|
|
2
27
|
export class YetterImageClient {
|
|
3
28
|
constructor(options) {
|
|
4
29
|
if (!options.apiKey) {
|
|
@@ -10,66 +35,131 @@ export class YetterImageClient {
|
|
|
10
35
|
getApiEndpoint() {
|
|
11
36
|
return this.endpoint;
|
|
12
37
|
}
|
|
38
|
+
getAuthHeaders() {
|
|
39
|
+
return {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
Authorization: this.apiKey,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async readErrorBody(res) {
|
|
45
|
+
try {
|
|
46
|
+
const text = await res.text();
|
|
47
|
+
return text || res.statusText || "Unknown API error";
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return res.statusText || "Unknown API error";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async requestJson(url, init, options = {}) {
|
|
54
|
+
var _a, _b, _c;
|
|
55
|
+
const maxRetries = (_a = options.maxRetries) !== null && _a !== void 0 ? _a : 0;
|
|
56
|
+
const timeoutMs = (_b = options.timeoutMs) !== null && _b !== void 0 ? _b : DEFAULT_TIMEOUT_MS;
|
|
57
|
+
const errorPrefix = (_c = options.errorPrefix) !== null && _c !== void 0 ? _c : "API error";
|
|
58
|
+
for (let attempt = 0;; attempt += 1) {
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
61
|
+
try {
|
|
62
|
+
const res = await fetch(url, {
|
|
63
|
+
...init,
|
|
64
|
+
signal: controller.signal,
|
|
65
|
+
});
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
const errorText = await this.readErrorBody(res);
|
|
68
|
+
const shouldRetry = attempt < maxRetries && RETRYABLE_STATUS_CODES.has(res.status);
|
|
69
|
+
if (shouldRetry) {
|
|
70
|
+
await sleep(getRetryDelayMs(attempt));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`${errorPrefix} (${res.status}): ${errorText}`);
|
|
74
|
+
}
|
|
75
|
+
return (await res.json());
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const retryableNetworkError = isAbortError(error) || isLikelyNetworkError(error);
|
|
79
|
+
const shouldRetry = attempt < maxRetries && retryableNetworkError;
|
|
80
|
+
if (shouldRetry) {
|
|
81
|
+
await sleep(getRetryDelayMs(attempt));
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (isAbortError(error)) {
|
|
85
|
+
throw new Error(`${errorPrefix}: request timed out after ${timeoutMs}ms`);
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
clearTimeout(timeout);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
13
94
|
async generate(body) {
|
|
95
|
+
if (!body.model) {
|
|
96
|
+
throw new Error("`model` is required in generate request body");
|
|
97
|
+
}
|
|
14
98
|
const url = `${this.endpoint}/${body.model}`;
|
|
15
|
-
|
|
99
|
+
return this.requestJson(url, {
|
|
16
100
|
method: "POST",
|
|
17
|
-
headers:
|
|
18
|
-
"Content-Type": "application/json",
|
|
19
|
-
Authorization: this.apiKey,
|
|
20
|
-
},
|
|
101
|
+
headers: this.getAuthHeaders(),
|
|
21
102
|
body: JSON.stringify(body),
|
|
103
|
+
}, {
|
|
104
|
+
maxRetries: 0,
|
|
22
105
|
});
|
|
23
|
-
if (!res.ok) {
|
|
24
|
-
const errorText = await res.text();
|
|
25
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
26
|
-
}
|
|
27
|
-
return (await res.json());
|
|
28
106
|
}
|
|
29
107
|
async getStatus(body) {
|
|
30
108
|
const url = new URL(body.url);
|
|
31
109
|
if (body.logs) {
|
|
32
110
|
url.searchParams.append('logs', '1');
|
|
33
111
|
}
|
|
34
|
-
|
|
112
|
+
return this.requestJson(url.toString(), {
|
|
35
113
|
method: "GET",
|
|
36
|
-
headers:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
},
|
|
114
|
+
headers: this.getAuthHeaders(),
|
|
115
|
+
}, {
|
|
116
|
+
maxRetries: 4,
|
|
40
117
|
});
|
|
41
|
-
if (!res.ok) {
|
|
42
|
-
const errorText = await res.text();
|
|
43
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
44
|
-
}
|
|
45
|
-
return (await res.json());
|
|
46
118
|
}
|
|
47
119
|
async cancel(body) {
|
|
48
|
-
|
|
120
|
+
return this.requestJson(body.url, {
|
|
49
121
|
method: "PUT",
|
|
50
|
-
headers:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
122
|
+
headers: this.getAuthHeaders(),
|
|
123
|
+
}, {
|
|
124
|
+
maxRetries: 3,
|
|
54
125
|
});
|
|
55
|
-
if (!res.ok) {
|
|
56
|
-
const errorText = await res.text();
|
|
57
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
58
|
-
}
|
|
59
|
-
return (await res.json());
|
|
60
126
|
}
|
|
61
127
|
async getResponse(body) {
|
|
62
|
-
|
|
128
|
+
return this.requestJson(body.url, {
|
|
63
129
|
method: "GET",
|
|
64
|
-
headers:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
130
|
+
headers: this.getAuthHeaders(),
|
|
131
|
+
}, {
|
|
132
|
+
maxRetries: 4,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Request presigned URL(s) for file upload
|
|
137
|
+
* @param body Upload request parameters
|
|
138
|
+
* @returns Presigned URL response with mode (single/multipart)
|
|
139
|
+
*/
|
|
140
|
+
async getUploadUrl(body) {
|
|
141
|
+
return this.requestJson(`${this.endpoint}/uploads`, {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: this.getAuthHeaders(),
|
|
144
|
+
body: JSON.stringify(body),
|
|
145
|
+
}, {
|
|
146
|
+
maxRetries: 0,
|
|
147
|
+
errorPrefix: "Upload URL request failed",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Notify server that upload is complete
|
|
152
|
+
* @param body Completion request with S3 key
|
|
153
|
+
* @returns Uploaded file metadata with public URL
|
|
154
|
+
*/
|
|
155
|
+
async uploadComplete(body) {
|
|
156
|
+
return this.requestJson(`${this.endpoint}/uploads/complete`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: this.getAuthHeaders(),
|
|
159
|
+
body: JSON.stringify(body),
|
|
160
|
+
}, {
|
|
161
|
+
maxRetries: 0,
|
|
162
|
+
errorPrefix: "Upload completion failed",
|
|
68
163
|
});
|
|
69
|
-
if (!res.ok) {
|
|
70
|
-
const errorText = await res.text();
|
|
71
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
72
|
-
}
|
|
73
|
-
return (await res.json());
|
|
74
164
|
}
|
|
75
165
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
|
-
import { ClientOptions, GetResponseResponse, SubscribeOptions, GenerateResponse, SubmitQueueOptions, GetResultOptions, GetResultResponse, StatusOptions, StatusResponse, StreamOptions, YetterStream } from "./types.js";
|
|
1
|
+
import { ClientOptions, GetResponseResponse, SubscribeOptions, GenerateResponse, SubmitQueueOptions, GetResultOptions, GetResultResponse, StatusOptions, StatusResponse, StreamOptions, YetterStream, UploadOptions, UploadCompleteResponse } from "./types.js";
|
|
2
|
+
export declare class YetterClient {
|
|
3
|
+
private apiKey;
|
|
4
|
+
private endpoint;
|
|
5
|
+
constructor(options?: Partial<ClientOptions>);
|
|
6
|
+
private setApiKey;
|
|
7
|
+
private assertApiKeyConfigured;
|
|
8
|
+
private createApiClient;
|
|
9
|
+
private getUploadTimeoutMs;
|
|
10
|
+
private putWithTimeout;
|
|
11
|
+
configure(options: ClientOptions): void;
|
|
12
|
+
subscribe(model: string, options: SubscribeOptions): Promise<GetResponseResponse>;
|
|
13
|
+
readonly queue: {
|
|
14
|
+
submit: (model: string, options: SubmitQueueOptions) => Promise<GenerateResponse>;
|
|
15
|
+
status: (model: string, options: StatusOptions) => Promise<StatusResponse>;
|
|
16
|
+
result: (model: string, options: GetResultOptions) => Promise<GetResultResponse>;
|
|
17
|
+
};
|
|
18
|
+
stream(model: string, options: StreamOptions): Promise<YetterStream>;
|
|
19
|
+
uploadFile(fileOrPath: string | File | Blob, options?: UploadOptions): Promise<UploadCompleteResponse>;
|
|
20
|
+
uploadBlob(file: File | Blob, options?: UploadOptions): Promise<UploadCompleteResponse>;
|
|
21
|
+
private _uploadFromPath;
|
|
22
|
+
private _uploadFromBlob;
|
|
23
|
+
private _uploadFileSingle;
|
|
24
|
+
private _uploadFileMultipart;
|
|
25
|
+
private _uploadBlobSingle;
|
|
26
|
+
private _uploadBlobMultipart;
|
|
27
|
+
}
|
|
2
28
|
export declare class yetter {
|
|
3
|
-
private static
|
|
4
|
-
private static endpoint;
|
|
29
|
+
private static client;
|
|
5
30
|
static configure(options: ClientOptions): void;
|
|
6
31
|
static subscribe(model: string, options: SubscribeOptions): Promise<GetResponseResponse>;
|
|
7
32
|
static queue: {
|
|
@@ -10,4 +35,6 @@ export declare class yetter {
|
|
|
10
35
|
result: (model: string, options: GetResultOptions) => Promise<GetResultResponse>;
|
|
11
36
|
};
|
|
12
37
|
static stream(model: string, options: StreamOptions): Promise<YetterStream>;
|
|
38
|
+
static uploadFile(fileOrPath: string | File | Blob, options?: UploadOptions): Promise<UploadCompleteResponse>;
|
|
39
|
+
static uploadBlob(file: File | Blob, options?: UploadOptions): Promise<UploadCompleteResponse>;
|
|
13
40
|
}
|