@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/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { YetterImageClient } from "./api.js";
|
|
2
2
|
export * from "./types.js";
|
|
3
|
-
export { yetter } from "./client.js";
|
|
3
|
+
export { yetter, YetterClient } from "./client.js";
|
|
4
|
+
export type { UploadOptions, GetUploadUrlRequest, GetUploadUrlResponse, UploadCompleteRequest, UploadCompleteResponse, } from "./types.js";
|
package/dist/index.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -58,6 +58,7 @@ export interface SubscribeOptions {
|
|
|
58
58
|
input: Omit<GenerateRequest, 'model'>;
|
|
59
59
|
logs?: boolean;
|
|
60
60
|
onQueueUpdate?: (update: GetStatusResponse) => void;
|
|
61
|
+
pollIntervalMs?: number;
|
|
61
62
|
}
|
|
62
63
|
export interface SubmitQueueOptions {
|
|
63
64
|
input: Omit<GenerateRequest, 'model'>;
|
|
@@ -90,3 +91,65 @@ export interface YetterStream extends AsyncIterable<StreamEvent> {
|
|
|
90
91
|
cancel(): Promise<void>;
|
|
91
92
|
getRequestId(): string;
|
|
92
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Options for yetter.uploadFile() and yetter.uploadBlob()
|
|
96
|
+
*/
|
|
97
|
+
export interface UploadOptions {
|
|
98
|
+
/** Optional callback for upload progress (0-100) */
|
|
99
|
+
onProgress?: (progress: number) => void;
|
|
100
|
+
/** Optional upload timeout in milliseconds (default: 5 minutes) */
|
|
101
|
+
timeout?: number;
|
|
102
|
+
/** Optional custom filename (browser File uploads) */
|
|
103
|
+
filename?: string;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Request to get presigned upload URL(s)
|
|
107
|
+
*/
|
|
108
|
+
export interface GetUploadUrlRequest {
|
|
109
|
+
/** Original filename with extension */
|
|
110
|
+
file_name: string;
|
|
111
|
+
/** MIME type (e.g., "image/jpeg") */
|
|
112
|
+
content_type: string;
|
|
113
|
+
/** File size in bytes */
|
|
114
|
+
size: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Response containing presigned URL(s) for upload
|
|
118
|
+
*/
|
|
119
|
+
export interface GetUploadUrlResponse {
|
|
120
|
+
/** Upload mode: "single" for small files, "multipart" for large files */
|
|
121
|
+
mode: "single" | "multipart";
|
|
122
|
+
/** S3 object key for tracking */
|
|
123
|
+
key: string;
|
|
124
|
+
/** Presigned PUT URL (single mode only) */
|
|
125
|
+
put_url?: string;
|
|
126
|
+
/** Size of each part in bytes (multipart mode only) */
|
|
127
|
+
part_size?: number;
|
|
128
|
+
/** Array of part URLs with part numbers (multipart mode only) */
|
|
129
|
+
part_urls?: Array<{
|
|
130
|
+
part_number: number;
|
|
131
|
+
url: string;
|
|
132
|
+
}>;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Request to notify upload completion
|
|
136
|
+
*/
|
|
137
|
+
export interface UploadCompleteRequest {
|
|
138
|
+
/** S3 object key from GetUploadUrlResponse */
|
|
139
|
+
key: string;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Response after successful upload completion
|
|
143
|
+
*/
|
|
144
|
+
export interface UploadCompleteResponse {
|
|
145
|
+
/** Public URL to access the uploaded file */
|
|
146
|
+
url: string;
|
|
147
|
+
/** S3 object key */
|
|
148
|
+
key: string;
|
|
149
|
+
/** Optional metadata */
|
|
150
|
+
metadata?: {
|
|
151
|
+
size: number;
|
|
152
|
+
content_type: string;
|
|
153
|
+
uploaded_at?: string;
|
|
154
|
+
};
|
|
155
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yetter/client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsc",
|
|
7
|
+
"test": "npm run build && node --test tests/*.test.mjs",
|
|
7
8
|
"test:submit": "node --loader ts-node/esm examples/submit.ts",
|
|
8
9
|
"test:subscribe": "node --loader ts-node/esm examples/subscribe.ts",
|
|
9
10
|
"test:stream": "node --loader ts-node/esm examples/stream.ts",
|
|
@@ -11,6 +12,10 @@
|
|
|
11
12
|
},
|
|
12
13
|
"main": "dist/index.js",
|
|
13
14
|
"types": "dist/index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
14
19
|
"exports": {
|
|
15
20
|
".": {
|
|
16
21
|
"import": "./dist/index.js",
|
|
@@ -23,9 +28,11 @@
|
|
|
23
28
|
"description": "",
|
|
24
29
|
"dependencies": {
|
|
25
30
|
"@types/eventsource": "^1.1.15",
|
|
31
|
+
"@types/mime-types": "^3.0.1",
|
|
26
32
|
"cross-fetch": "^4.1.0",
|
|
27
33
|
"event-source-polyfill": "^1.0.31",
|
|
28
|
-
"eventsource": "^4.0.0"
|
|
34
|
+
"eventsource": "^4.0.0",
|
|
35
|
+
"mime-types": "^3.0.2"
|
|
29
36
|
},
|
|
30
37
|
"devDependencies": {
|
|
31
38
|
"@types/event-source-polyfill": "^1.0.5",
|
package/examples/stream.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { yetter } from "../src/client.js";
|
|
2
|
-
|
|
3
|
-
async function main() {
|
|
4
|
-
const model = "ytr-ai/flux/dev";
|
|
5
|
-
console.log("\n--- Starting Stream Test ---");
|
|
6
|
-
console.time("Stream Test");
|
|
7
|
-
let streamRequestId = "";
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
const streamInstance = await yetter.stream(model, {
|
|
11
|
-
input: {
|
|
12
|
-
prompt: "a bioluminescent forest at night, fantasy art",
|
|
13
|
-
num_inference_steps: 28,
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
streamRequestId = streamInstance.getRequestId();
|
|
17
|
-
console.log(`[${new Date().toLocaleTimeString()}] Stream initiated for Request ID: ${streamRequestId}`);
|
|
18
|
-
|
|
19
|
-
// Iterate over stream events
|
|
20
|
-
for await (const event of streamInstance) {
|
|
21
|
-
console.log(`[${new Date().toLocaleTimeString()}][STREAM EVENT - ${streamRequestId}] Status: ${event.status}, QPos: ${event.queue_position}`);
|
|
22
|
-
if (event.logs && event.logs.length > 0) {
|
|
23
|
-
console.log(` Logs for ${streamRequestId}:`);
|
|
24
|
-
event.logs.forEach(log => console.log(` - ${log.message}`));
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
console.log(`[${new Date().toLocaleTimeString()}] Stream for ${streamRequestId} finished iterating events.`);
|
|
28
|
-
|
|
29
|
-
// Wait for the final result from the done() method
|
|
30
|
-
console.log(`[${new Date().toLocaleTimeString()}] Waiting for done() on stream for ${streamRequestId}...`);
|
|
31
|
-
const result = await streamInstance.done();
|
|
32
|
-
console.log("\n--- Stream Test Final Result ---");
|
|
33
|
-
console.log(`Request ID: ${streamRequestId}`);
|
|
34
|
-
if (result.images && result.images.length > 0) {
|
|
35
|
-
console.log("Generated Images:", result.images);
|
|
36
|
-
} else {
|
|
37
|
-
console.log("No images in final result.");
|
|
38
|
-
}''
|
|
39
|
-
console.log("Original Prompt:", result.prompt);
|
|
40
|
-
console.timeEnd("Stream Test");
|
|
41
|
-
} catch (err: any) {
|
|
42
|
-
console.error(`\n--- Stream Test Failed (Request ID: ${streamRequestId || 'UNKNOWN'}) ---`);
|
|
43
|
-
console.error("Error during stream test:", err.message || err);
|
|
44
|
-
if (err.stack) {
|
|
45
|
-
console.error(err.stack);
|
|
46
|
-
}
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
main().catch(err => {
|
|
52
|
-
console.error("Unhandled error in main:", err);
|
|
53
|
-
process.exit(1);
|
|
54
|
-
});
|
package/examples/submit.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { yetter } from "../src/client.js";
|
|
2
|
-
|
|
3
|
-
async function main() {
|
|
4
|
-
const model = "ytr-ai/flux/dev";
|
|
5
|
-
// Queue Submit test
|
|
6
|
-
try {
|
|
7
|
-
console.log("\n--- Starting Queue Submit Test ---");
|
|
8
|
-
const { request_id, status, queue_position } = await yetter.queue.submit(model, {
|
|
9
|
-
input: {
|
|
10
|
-
prompt: "a fluffy white kitten playing with a yarn ball",
|
|
11
|
-
num_inference_steps: 28,
|
|
12
|
-
},
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
console.log("\n--- Queue Submit Test Result ---");
|
|
16
|
-
console.log("Request ID:", request_id);
|
|
17
|
-
console.log("Status:", status);
|
|
18
|
-
console.log("Queue Position:", queue_position);
|
|
19
|
-
|
|
20
|
-
if (request_id) {
|
|
21
|
-
console.log(`\n--- Polling for status of Request ID: ${request_id} (3-minute timeout) ---`);
|
|
22
|
-
|
|
23
|
-
const startTime = Date.now();
|
|
24
|
-
const timeoutMilliseconds = 3 * 60 * 1000; // 3 minutes
|
|
25
|
-
const pollIntervalMilliseconds = 10 * 1000; // Poll every 10 seconds
|
|
26
|
-
let success = false;
|
|
27
|
-
|
|
28
|
-
while (Date.now() - startTime < timeoutMilliseconds) {
|
|
29
|
-
try {
|
|
30
|
-
const statusResult = await yetter.queue.status(model, {
|
|
31
|
-
requestId: request_id,
|
|
32
|
-
});
|
|
33
|
-
const currentStatus = statusResult.data.status;
|
|
34
|
-
console.log(`[${new Date().toLocaleTimeString()}] Request ${request_id} status: ${currentStatus}, Queue Position: ${statusResult.data.queue_position}`);
|
|
35
|
-
|
|
36
|
-
if (currentStatus === "COMPLETED") {
|
|
37
|
-
console.log("Status is COMPLETED. Fetching final result...");
|
|
38
|
-
const finalResult = await yetter.queue.result(model, {
|
|
39
|
-
requestId: request_id,
|
|
40
|
-
});
|
|
41
|
-
console.log("\n--- Get Result Test Succeeded ---");
|
|
42
|
-
console.log("Request ID:", finalResult.requestId);
|
|
43
|
-
console.log("Image Data:", finalResult.data.images);
|
|
44
|
-
console.log("Prompt:", finalResult.data.prompt);
|
|
45
|
-
success = true;
|
|
46
|
-
break; // Exit loop on success
|
|
47
|
-
} else if (currentStatus === "FAILED") {
|
|
48
|
-
console.error(`Request ${request_id} FAILED. Logs:`, statusResult.data.logs);
|
|
49
|
-
break; // Exit loop on failure
|
|
50
|
-
}
|
|
51
|
-
// For other statuses (e.g., IN_QUEUE, IN_PROGRESS), continue polling
|
|
52
|
-
|
|
53
|
-
} catch (pollError: any) {
|
|
54
|
-
console.error(`Error during status polling for ${request_id} (will retry):`, pollError.message || pollError);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (Date.now() - startTime + pollIntervalMilliseconds >= timeoutMilliseconds) {
|
|
58
|
-
if (!success) console.error("Timeout approaching, last poll cycle completed or error occurred.");
|
|
59
|
-
break; // Don't wait if the next interval exceeds timeout
|
|
60
|
-
}
|
|
61
|
-
await new Promise(resolve => setTimeout(resolve, pollIntervalMilliseconds));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!success) {
|
|
65
|
-
console.error(`\n--- Polling Timed Out or Failed for Request ID: ${request_id} ---`);
|
|
66
|
-
console.error(`Failed to get a COMPLETED status for ${request_id} within 3 minutes.`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
} catch (err: any) {
|
|
71
|
-
console.error("\n--- Queue Submit Test Failed ---");
|
|
72
|
-
console.error("Error during queue submit:", err.message || err);
|
|
73
|
-
if (err.stack) {
|
|
74
|
-
console.error(err.stack);
|
|
75
|
-
}
|
|
76
|
-
process.exit(1);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
main();
|
package/examples/subscribe.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { yetter } from "../dist/index.js";
|
|
2
|
-
|
|
3
|
-
async function main() {
|
|
4
|
-
const model = "ytr-ai/flux/dev";
|
|
5
|
-
// Subscribe test
|
|
6
|
-
try {
|
|
7
|
-
console.log("\n--- Starting Subscribe Test ---");
|
|
8
|
-
console.time("Subscribe Test");
|
|
9
|
-
const result = await yetter.subscribe(model, {
|
|
10
|
-
input: {
|
|
11
|
-
prompt: "a vibrant coral reef, underwater photography",
|
|
12
|
-
num_inference_steps: 28,
|
|
13
|
-
},
|
|
14
|
-
logs: true,
|
|
15
|
-
onQueueUpdate: (update) => {
|
|
16
|
-
console.log(`[Queue Update] Status: ${update.status}, Position: ${update.queue_position}`);
|
|
17
|
-
if (update.status === "IN_PROGRESS" && update.logs) {
|
|
18
|
-
console.log("Logs:");
|
|
19
|
-
update.logs.map((log) => log.message).forEach(logMessage => console.log(` - ${logMessage}`));
|
|
20
|
-
} else if (update.status === "COMPLETED") {
|
|
21
|
-
console.log("Processing completed!");
|
|
22
|
-
} else if (update.status === "FAILED") {
|
|
23
|
-
console.error("Processing failed. Logs:", update.logs);
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
console.log("\n--- Subscribe Test Result ---");
|
|
29
|
-
console.log("Results:", result);
|
|
30
|
-
console.timeEnd("Subscribe Test");
|
|
31
|
-
} catch (err: any) {
|
|
32
|
-
console.error("\n--- Subscribe Test Failed ---");
|
|
33
|
-
console.error("Error during subscribe:", err.message || err);
|
|
34
|
-
if (err.stack) {
|
|
35
|
-
console.error(err.stack);
|
|
36
|
-
}
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
main();
|
package/src/api.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import fetch from "cross-fetch";
|
|
2
|
-
import {
|
|
3
|
-
ClientOptions,
|
|
4
|
-
GenerateRequest,
|
|
5
|
-
GenerateResponse,
|
|
6
|
-
GetStatusRequest,
|
|
7
|
-
GetStatusResponse,
|
|
8
|
-
CancelRequest,
|
|
9
|
-
CancelResponse,
|
|
10
|
-
GetResponseRequest,
|
|
11
|
-
GetResponseResponse,
|
|
12
|
-
} from "./types";
|
|
13
|
-
|
|
14
|
-
export class YetterImageClient {
|
|
15
|
-
private apiKey: string;
|
|
16
|
-
private endpoint: string;
|
|
17
|
-
|
|
18
|
-
constructor(options: ClientOptions) {
|
|
19
|
-
if (!options.apiKey) {
|
|
20
|
-
throw new Error("`apiKey` is required");
|
|
21
|
-
}
|
|
22
|
-
this.apiKey = options.apiKey;
|
|
23
|
-
this.endpoint = options.endpoint || "https://api.yetter.ai";
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public getApiEndpoint(): string {
|
|
27
|
-
return this.endpoint;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public async generate(
|
|
31
|
-
body: GenerateRequest
|
|
32
|
-
): Promise<GenerateResponse> {
|
|
33
|
-
const url = `${this.endpoint}/${body.model}`;
|
|
34
|
-
const res = await fetch(url, {
|
|
35
|
-
method: "POST",
|
|
36
|
-
headers: {
|
|
37
|
-
"Content-Type": "application/json",
|
|
38
|
-
Authorization: this.apiKey,
|
|
39
|
-
},
|
|
40
|
-
body: JSON.stringify(body),
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
if (!res.ok) {
|
|
44
|
-
const errorText = await res.text();
|
|
45
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return (await res.json()) as GenerateResponse;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
public async getStatus(body: GetStatusRequest): Promise<GetStatusResponse> {
|
|
52
|
-
const url = new URL(body.url);
|
|
53
|
-
if (body.logs) {
|
|
54
|
-
url.searchParams.append('logs', '1');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const res = await fetch(url.toString(), {
|
|
58
|
-
method: "GET",
|
|
59
|
-
headers: {
|
|
60
|
-
"Content-Type": "application/json",
|
|
61
|
-
Authorization: this.apiKey,
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
if (!res.ok) {
|
|
66
|
-
const errorText = await res.text();
|
|
67
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return (await res.json()) as GetStatusResponse;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
public async cancel(body: CancelRequest): Promise<CancelResponse> {
|
|
74
|
-
const res = await fetch(body.url, {
|
|
75
|
-
method: "PUT",
|
|
76
|
-
headers: {
|
|
77
|
-
"Content-Type": "application/json",
|
|
78
|
-
Authorization: this.apiKey,
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
if (!res.ok) {
|
|
83
|
-
const errorText = await res.text();
|
|
84
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return (await res.json()) as CancelResponse;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
public async getResponse(body: GetResponseRequest): Promise<GetResponseResponse> {
|
|
91
|
-
const res = await fetch(body.url, {
|
|
92
|
-
method: "GET",
|
|
93
|
-
headers: {
|
|
94
|
-
"Content-Type": "application/json",
|
|
95
|
-
Authorization: this.apiKey,
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (!res.ok) {
|
|
100
|
-
const errorText = await res.text();
|
|
101
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
102
|
-
}
|
|
103
|
-
return (await res.json()) as GetResponseResponse;
|
|
104
|
-
}
|
|
105
|
-
}
|