@yetter/client 0.0.12 → 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 +52 -3
- package/dist/api.d.ts +3 -0
- package/dist/api.js +110 -60
- package/dist/client.d.ts +27 -52
- package/dist/client.js +264 -262
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types.d.ts +3 -0
- package/package.json +6 -1
- package/bowow2.jpeg +0 -0
- package/examples/stream.ts +0 -54
- package/examples/submit.ts +0 -80
- package/examples/subscribe.ts +0 -41
- package/examples/upload-and-generate.ts +0 -39
- package/src/api.ts +0 -159
- package/src/client.ts +0 -697
- package/src/index.ts +0 -12
- package/src/types.ts +0 -192
- package/tsconfig.json +0 -13
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +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
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'>;
|
|
@@ -96,6 +97,8 @@ export interface YetterStream extends AsyncIterable<StreamEvent> {
|
|
|
96
97
|
export interface UploadOptions {
|
|
97
98
|
/** Optional callback for upload progress (0-100) */
|
|
98
99
|
onProgress?: (progress: number) => void;
|
|
100
|
+
/** Optional upload timeout in milliseconds (default: 5 minutes) */
|
|
101
|
+
timeout?: number;
|
|
99
102
|
/** Optional custom filename (browser File uploads) */
|
|
100
103
|
filename?: string;
|
|
101
104
|
}
|
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",
|
package/bowow2.jpeg
DELETED
|
Binary file
|
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 === "ERROR") {
|
|
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 === "ERROR") {
|
|
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();
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { yetter } from "../src/client.js";
|
|
2
|
-
|
|
3
|
-
async function main() {
|
|
4
|
-
// Configure with API key
|
|
5
|
-
yetter.configure({
|
|
6
|
-
apiKey: process.env.YTR_API_KEY || "your_api_key_here"
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
console.log("Step 1: Uploading input image...");
|
|
10
|
-
|
|
11
|
-
// Replace with your actual image path
|
|
12
|
-
const imagePath = "./test-image.jpg";
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
const uploadResult = await yetter.uploadFile(imagePath, {
|
|
16
|
-
onProgress: (pct) => console.log(` Upload progress: ${pct}%`),
|
|
17
|
-
});
|
|
18
|
-
console.log(`✓ Uploaded: ${uploadResult.url}\n`);
|
|
19
|
-
|
|
20
|
-
console.log("Step 2: Generating with uploaded image...");
|
|
21
|
-
const genResult = await yetter.subscribe("ytr-ai/qwen/image-edit/i2i", {
|
|
22
|
-
input: {
|
|
23
|
-
prompt: "Transform to watercolor painting style",
|
|
24
|
-
image_url: [uploadResult.url],
|
|
25
|
-
num_inference_steps: 28,
|
|
26
|
-
},
|
|
27
|
-
onQueueUpdate: (status) => {
|
|
28
|
-
console.log(` Status: ${status.status}, Queue: ${status.queue_position}`);
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
console.log("\n✓ Generation complete!");
|
|
33
|
-
console.log("Generated images:", genResult.images);
|
|
34
|
-
} catch (error: any) {
|
|
35
|
-
console.error("Error:", error.message);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
main().catch(console.error);
|
package/src/api.ts
DELETED
|
@@ -1,159 +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
|
-
GetUploadUrlRequest,
|
|
13
|
-
GetUploadUrlResponse,
|
|
14
|
-
UploadCompleteRequest,
|
|
15
|
-
UploadCompleteResponse,
|
|
16
|
-
} from "./types";
|
|
17
|
-
|
|
18
|
-
export class YetterImageClient {
|
|
19
|
-
private apiKey: string;
|
|
20
|
-
private endpoint: string;
|
|
21
|
-
|
|
22
|
-
constructor(options: ClientOptions) {
|
|
23
|
-
if (!options.apiKey) {
|
|
24
|
-
throw new Error("`apiKey` is required");
|
|
25
|
-
}
|
|
26
|
-
this.apiKey = options.apiKey;
|
|
27
|
-
this.endpoint = options.endpoint || "https://api.yetter.ai";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public getApiEndpoint(): string {
|
|
31
|
-
return this.endpoint;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public async generate(
|
|
35
|
-
body: GenerateRequest
|
|
36
|
-
): Promise<GenerateResponse> {
|
|
37
|
-
const url = `${this.endpoint}/${body.model}`;
|
|
38
|
-
const res = await fetch(url, {
|
|
39
|
-
method: "POST",
|
|
40
|
-
headers: {
|
|
41
|
-
"Content-Type": "application/json",
|
|
42
|
-
Authorization: this.apiKey,
|
|
43
|
-
},
|
|
44
|
-
body: JSON.stringify(body),
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
if (!res.ok) {
|
|
48
|
-
const errorText = await res.text();
|
|
49
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return (await res.json()) as GenerateResponse;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public async getStatus(body: GetStatusRequest): Promise<GetStatusResponse> {
|
|
56
|
-
const url = new URL(body.url);
|
|
57
|
-
if (body.logs) {
|
|
58
|
-
url.searchParams.append('logs', '1');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const res = await fetch(url.toString(), {
|
|
62
|
-
method: "GET",
|
|
63
|
-
headers: {
|
|
64
|
-
"Content-Type": "application/json",
|
|
65
|
-
Authorization: this.apiKey,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
if (!res.ok) {
|
|
70
|
-
const errorText = await res.text();
|
|
71
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return (await res.json()) as GetStatusResponse;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
public async cancel(body: CancelRequest): Promise<CancelResponse> {
|
|
78
|
-
const res = await fetch(body.url, {
|
|
79
|
-
method: "PUT",
|
|
80
|
-
headers: {
|
|
81
|
-
"Content-Type": "application/json",
|
|
82
|
-
Authorization: this.apiKey,
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
if (!res.ok) {
|
|
87
|
-
const errorText = await res.text();
|
|
88
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return (await res.json()) as CancelResponse;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public async getResponse(body: GetResponseRequest): Promise<GetResponseResponse> {
|
|
95
|
-
const res = await fetch(body.url, {
|
|
96
|
-
method: "GET",
|
|
97
|
-
headers: {
|
|
98
|
-
"Content-Type": "application/json",
|
|
99
|
-
Authorization: this.apiKey,
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
if (!res.ok) {
|
|
104
|
-
const errorText = await res.text();
|
|
105
|
-
throw new Error(`API error (${res.status}): ${errorText}`);
|
|
106
|
-
}
|
|
107
|
-
return (await res.json()) as GetResponseResponse;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Request presigned URL(s) for file upload
|
|
112
|
-
* @param body Upload request parameters
|
|
113
|
-
* @returns Presigned URL response with mode (single/multipart)
|
|
114
|
-
*/
|
|
115
|
-
public async getUploadUrl(
|
|
116
|
-
body: GetUploadUrlRequest
|
|
117
|
-
): Promise<GetUploadUrlResponse> {
|
|
118
|
-
const res = await fetch(`${this.endpoint}/uploads`, {
|
|
119
|
-
method: "POST",
|
|
120
|
-
headers: {
|
|
121
|
-
"Content-Type": "application/json",
|
|
122
|
-
Authorization: this.apiKey,
|
|
123
|
-
},
|
|
124
|
-
body: JSON.stringify(body),
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if (!res.ok) {
|
|
128
|
-
const errorText = await res.text();
|
|
129
|
-
throw new Error(`Upload URL request failed (${res.status}): ${errorText}`);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return (await res.json()) as GetUploadUrlResponse;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Notify server that upload is complete
|
|
137
|
-
* @param body Completion request with S3 key
|
|
138
|
-
* @returns Uploaded file metadata with public URL
|
|
139
|
-
*/
|
|
140
|
-
public async uploadComplete(
|
|
141
|
-
body: UploadCompleteRequest
|
|
142
|
-
): Promise<UploadCompleteResponse> {
|
|
143
|
-
const res = await fetch(`${this.endpoint}/uploads/complete`, {
|
|
144
|
-
method: "POST",
|
|
145
|
-
headers: {
|
|
146
|
-
"Content-Type": "application/json",
|
|
147
|
-
Authorization: this.apiKey,
|
|
148
|
-
},
|
|
149
|
-
body: JSON.stringify(body),
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
if (!res.ok) {
|
|
153
|
-
const errorText = await res.text();
|
|
154
|
-
throw new Error(`Upload completion failed (${res.status}): ${errorText}`);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return (await res.json()) as UploadCompleteResponse;
|
|
158
|
-
}
|
|
159
|
-
}
|