@yetter/client 0.0.3 → 0.0.5

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 CHANGED
@@ -10,12 +10,26 @@ npm install @yetter/client
10
10
 
11
11
  ## Authentication
12
12
 
13
- The client requires a Yetter API key for authentication. Ensure you have the `YTR_API_KEY` environment variable set:
13
+ 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:
14
14
 
15
15
  ```bash
16
16
  export YTR_API_KEY="your_api_key_here"
17
+ export REACT_APP_YTR_API_KEY="your_api_key_here"
17
18
  ```
18
19
 
20
+ Alternatively, you can configure the API key (and optionally the API endpoint) programmatically using the `yetter.configure()` method. This is useful if you prefer not to use environment variables or need to set the key at runtime.
21
+
22
+ ```typescript
23
+ import { yetter } from "@yetter/client";
24
+
25
+ yetter.configure({
26
+ apiKey: "your_api_key_here",
27
+ // endpoint: "https://custom.api.yetter.ai" // if you need to override the default endpoint
28
+ });
29
+ ```
30
+
31
+ 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
+
19
33
  ## Core Functionalities
20
34
 
21
35
  The client is available via the `yetter` object imported from `yetter-js` (or the relevant path to `client.js`/`client.ts` if used directly).
package/dist/api.js CHANGED
@@ -16,7 +16,7 @@ export class YetterImageClient {
16
16
  method: "POST",
17
17
  headers: {
18
18
  "Content-Type": "application/json",
19
- Authorization: `${this.apiKey}`,
19
+ Authorization: this.apiKey,
20
20
  },
21
21
  body: JSON.stringify(body),
22
22
  });
@@ -35,7 +35,7 @@ export class YetterImageClient {
35
35
  method: "GET",
36
36
  headers: {
37
37
  "Content-Type": "application/json",
38
- Authorization: `${this.apiKey}`,
38
+ Authorization: this.apiKey,
39
39
  },
40
40
  });
41
41
  if (!res.ok) {
@@ -49,7 +49,7 @@ export class YetterImageClient {
49
49
  method: "PUT",
50
50
  headers: {
51
51
  "Content-Type": "application/json",
52
- Authorization: `${this.apiKey}`,
52
+ Authorization: this.apiKey,
53
53
  },
54
54
  });
55
55
  if (!res.ok) {
@@ -63,7 +63,7 @@ export class YetterImageClient {
63
63
  method: "GET",
64
64
  headers: {
65
65
  "Content-Type": "application/json",
66
- Authorization: `${this.apiKey}`,
66
+ Authorization: this.apiKey,
67
67
  },
68
68
  });
69
69
  if (!res.ok) {
package/dist/client.d.ts CHANGED
@@ -1,5 +1,8 @@
1
- import { GetResponseResponse, SubscribeOptions, GenerateImageResponse, SubmitQueueOptions, GetResultOptions, GetResultResponse, StatusOptions, StatusResponse, StreamOptions, YetterStream } from "./types.js";
1
+ import { ClientOptions, GetResponseResponse, SubscribeOptions, GenerateImageResponse, SubmitQueueOptions, GetResultOptions, GetResultResponse, StatusOptions, StatusResponse, StreamOptions, YetterStream } from "./types.js";
2
2
  export declare class yetter {
3
+ private static apiKey;
4
+ private static endpoint;
5
+ static configure(options: ClientOptions): void;
3
6
  static subscribe(model: string, options: SubscribeOptions): Promise<GetResponseResponse>;
4
7
  static queue: {
5
8
  submit: (model: string, options: SubmitQueueOptions) => Promise<GenerateImageResponse>;
package/dist/client.js CHANGED
@@ -2,13 +2,27 @@ var _a;
2
2
  import { YetterImageClient } from "./api.js";
3
3
  import { EventSourcePolyfill } from 'event-source-polyfill';
4
4
  export class yetter {
5
+ static configure(options) {
6
+ if (options.apiKey) {
7
+ if (options.is_bearer) {
8
+ _a.apiKey = 'Bearer ' + options.apiKey;
9
+ }
10
+ else {
11
+ _a.apiKey = 'Key ' + options.apiKey;
12
+ }
13
+ }
14
+ if (options.endpoint) {
15
+ _a.endpoint = options.endpoint;
16
+ }
17
+ }
5
18
  static async subscribe(model, options) {
6
19
  var _b;
7
- if (!process.env.YTR_API_KEY && !process.env.REACT_APP_YTR_API_KEY) {
8
- throw new Error("YTR_API_KEY and REACT_APP_YTR_API_KEY are not set");
20
+ if (!_a.apiKey) {
21
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
9
22
  }
10
23
  const client = new YetterImageClient({
11
- apiKey: process.env.YTR_API_KEY || process.env.REACT_APP_YTR_API_KEY || "",
24
+ apiKey: _a.apiKey,
25
+ endpoint: _a.endpoint
12
26
  });
13
27
  const generateResponse = await client.generateImage({
14
28
  model: model,
@@ -55,20 +69,21 @@ export class yetter {
55
69
  return finalResponse;
56
70
  }
57
71
  static async stream(model, options) {
58
- if (!process.env.YTR_API_KEY) {
59
- throw new Error("YTR_API_KEY is not set");
72
+ if (!_a.apiKey) {
73
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
60
74
  }
61
- const apiClient = new YetterImageClient({
62
- apiKey: process.env.YTR_API_KEY,
75
+ const client = new YetterImageClient({
76
+ apiKey: _a.apiKey,
77
+ endpoint: _a.endpoint
63
78
  });
64
- const initialApiResponse = await apiClient.generateImage({
79
+ const initialApiResponse = await client.generateImage({
65
80
  model: model,
66
81
  ...options.input,
67
82
  });
68
83
  const requestId = initialApiResponse.request_id;
69
84
  const responseUrl = initialApiResponse.response_url;
70
85
  const cancelUrl = initialApiResponse.cancel_url;
71
- const sseStreamUrl = `${apiClient.getApiEndpoint()}/${model}/requests/${requestId}/status/stream`;
86
+ const sseStreamUrl = `${client.getApiEndpoint()}/${model}/requests/${requestId}/status/stream`;
72
87
  let eventSource;
73
88
  let streamEnded = false;
74
89
  // Setup the promise for the done() method
@@ -97,7 +112,7 @@ export class yetter {
97
112
  // Check for terminal events to resolve/reject the donePromise
98
113
  if (event.status === "COMPLETED") {
99
114
  streamEnded = true;
100
- apiClient.getResponse({ url: responseUrl })
115
+ client.getResponse({ url: responseUrl })
101
116
  .then(resolveDonePromise)
102
117
  .catch(rejectDonePromise)
103
118
  .finally(() => this.close());
@@ -154,7 +169,7 @@ export class yetter {
154
169
  }
155
170
  };
156
171
  eventSource = new EventSourcePolyfill(sseStreamUrl, {
157
- headers: { 'Authorization': `${process.env.YTR_API_KEY}` }
172
+ headers: { 'Authorization': `${_a.apiKey}` }
158
173
  });
159
174
  eventSource.onopen = (event) => {
160
175
  console.log("SSE Connection Opened:", event);
@@ -198,7 +213,7 @@ export class yetter {
198
213
  cancel: async () => {
199
214
  controller.close();
200
215
  try {
201
- await apiClient.cancel({ url: cancelUrl });
216
+ await client.cancel({ url: cancelUrl });
202
217
  console.log(`Stream for ${requestId} - underlying request cancelled.`);
203
218
  }
204
219
  catch (e) {
@@ -214,13 +229,16 @@ export class yetter {
214
229
  }
215
230
  }
216
231
  _a = yetter;
232
+ yetter.apiKey = 'Key ' + (process.env.YTR_API_KEY || process.env.REACT_APP_YTR_API_KEY || "");
233
+ yetter.endpoint = "https://api.yetter.ai";
217
234
  yetter.queue = {
218
235
  submit: async (model, options) => {
219
- if (!process.env.YTR_API_KEY) {
220
- throw new Error("YTR_API_KEY is not set");
236
+ if (!_a.apiKey) {
237
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
221
238
  }
222
239
  const client = new YetterImageClient({
223
- apiKey: process.env.YTR_API_KEY,
240
+ apiKey: _a.apiKey,
241
+ endpoint: _a.endpoint
224
242
  });
225
243
  const generateResponse = await client.generateImage({
226
244
  model: model,
@@ -229,11 +247,12 @@ yetter.queue = {
229
247
  return generateResponse;
230
248
  },
231
249
  status: async (model, options) => {
232
- if (!process.env.YTR_API_KEY) {
233
- throw new Error("YTR_API_KEY is not set");
250
+ if (!_a.apiKey) {
251
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
234
252
  }
235
253
  const client = new YetterImageClient({
236
- apiKey: process.env.YTR_API_KEY,
254
+ apiKey: _a.apiKey,
255
+ endpoint: _a.endpoint
237
256
  });
238
257
  const endpoint = client.getApiEndpoint();
239
258
  const statusUrl = `${endpoint}/${model}/requests/${options.requestId}/status`;
@@ -244,11 +263,12 @@ yetter.queue = {
244
263
  };
245
264
  },
246
265
  result: async (model, options) => {
247
- if (!process.env.YTR_API_KEY) {
248
- throw new Error("YTR_API_KEY is not set");
266
+ if (!_a.apiKey) {
267
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
249
268
  }
250
269
  const client = new YetterImageClient({
251
- apiKey: process.env.YTR_API_KEY,
270
+ apiKey: _a.apiKey,
271
+ endpoint: _a.endpoint
252
272
  });
253
273
  const endpoint = client.getApiEndpoint();
254
274
  const responseUrl = `${endpoint}/${model}/requests/${options.requestId}`;
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface ClientOptions {
2
2
  apiKey: string;
3
3
  endpoint?: string;
4
+ is_bearer?: boolean;
4
5
  }
5
6
  export interface GenerateImageRequest {
6
7
  model: string;
@@ -9,6 +9,7 @@ async function main() {
9
9
  const streamInstance = await yetter.stream(model, {
10
10
  input: {
11
11
  prompt: "a bioluminescent forest at night, fantasy art",
12
+ seed: 42
12
13
  // You can add other parameters like seed or num_inference_steps here
13
14
  },
14
15
  });
@@ -34,7 +35,7 @@ async function main() {
34
35
  console.log("Generated Images:", result.images);
35
36
  } else {
36
37
  console.log("No images in final result.");
37
- }
38
+ }''
38
39
  console.log("Original Prompt:", result.prompt);
39
40
 
40
41
  } catch (err: any) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yetter/client",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "tsc",
package/src/api.ts CHANGED
@@ -35,7 +35,7 @@ export class YetterImageClient {
35
35
  method: "POST",
36
36
  headers: {
37
37
  "Content-Type": "application/json",
38
- Authorization: `${this.apiKey}`,
38
+ Authorization: this.apiKey,
39
39
  },
40
40
  body: JSON.stringify(body),
41
41
  });
@@ -58,7 +58,7 @@ export class YetterImageClient {
58
58
  method: "GET",
59
59
  headers: {
60
60
  "Content-Type": "application/json",
61
- Authorization: `${this.apiKey}`,
61
+ Authorization: this.apiKey,
62
62
  },
63
63
  });
64
64
 
@@ -75,7 +75,7 @@ export class YetterImageClient {
75
75
  method: "PUT",
76
76
  headers: {
77
77
  "Content-Type": "application/json",
78
- Authorization: `${this.apiKey}`,
78
+ Authorization: this.apiKey,
79
79
  },
80
80
  });
81
81
 
@@ -92,7 +92,7 @@ export class YetterImageClient {
92
92
  method: "GET",
93
93
  headers: {
94
94
  "Content-Type": "application/json",
95
- Authorization: `${this.apiKey}`,
95
+ Authorization: this.apiKey,
96
96
  },
97
97
  });
98
98
 
package/src/client.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { YetterImageClient } from "./api.js";
2
2
  import { EventSourcePolyfill } from 'event-source-polyfill';
3
3
  import {
4
+ ClientOptions,
4
5
  GetStatusResponse,
5
6
  GetResponseResponse,
6
7
  SubscribeOptions,
@@ -16,15 +17,32 @@ import {
16
17
  } from "./types.js";
17
18
 
18
19
  export class yetter {
20
+ private static apiKey: string = 'Key ' + (process.env.YTR_API_KEY || process.env.REACT_APP_YTR_API_KEY || "");
21
+ private static endpoint: string = "https://api.yetter.ai";
22
+
23
+ public static configure(options: ClientOptions) {
24
+ if (options.apiKey) {
25
+ if (options.is_bearer) {
26
+ yetter.apiKey = 'Bearer ' + options.apiKey;
27
+ } else {
28
+ yetter.apiKey = 'Key ' + options.apiKey;
29
+ }
30
+ }
31
+ if (options.endpoint) {
32
+ yetter.endpoint = options.endpoint;
33
+ }
34
+ }
35
+
19
36
  public static async subscribe(
20
37
  model: string,
21
38
  options: SubscribeOptions
22
39
  ): Promise<GetResponseResponse> {
23
- if (!process.env.YTR_API_KEY && !process.env.REACT_APP_YTR_API_KEY) {
24
- throw new Error("YTR_API_KEY and REACT_APP_YTR_API_KEY are not set");
40
+ if (!yetter.apiKey) {
41
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
25
42
  }
26
43
  const client = new YetterImageClient({
27
- apiKey: process.env.YTR_API_KEY || process.env.REACT_APP_YTR_API_KEY || "",
44
+ apiKey: yetter.apiKey,
45
+ endpoint: yetter.endpoint
28
46
  });
29
47
 
30
48
  const generateResponse = await client.generateImage({
@@ -83,11 +101,12 @@ export class yetter {
83
101
  model: string,
84
102
  options: SubmitQueueOptions
85
103
  ): Promise<GenerateImageResponse> => {
86
- if (!process.env.YTR_API_KEY) {
87
- throw new Error("YTR_API_KEY is not set");
104
+ if (!yetter.apiKey) {
105
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
88
106
  }
89
107
  const client = new YetterImageClient({
90
- apiKey: process.env.YTR_API_KEY,
108
+ apiKey: yetter.apiKey,
109
+ endpoint: yetter.endpoint
91
110
  });
92
111
 
93
112
  const generateResponse = await client.generateImage({
@@ -102,11 +121,12 @@ export class yetter {
102
121
  model: string,
103
122
  options: StatusOptions
104
123
  ): Promise<StatusResponse> => {
105
- if (!process.env.YTR_API_KEY) {
106
- throw new Error("YTR_API_KEY is not set");
124
+ if (!yetter.apiKey) {
125
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
107
126
  }
108
127
  const client = new YetterImageClient({
109
- apiKey: process.env.YTR_API_KEY,
128
+ apiKey: yetter.apiKey,
129
+ endpoint: yetter.endpoint
110
130
  });
111
131
 
112
132
  const endpoint = client.getApiEndpoint();
@@ -125,11 +145,12 @@ export class yetter {
125
145
  model: string,
126
146
  options: GetResultOptions
127
147
  ): Promise<GetResultResponse> => {
128
- if (!process.env.YTR_API_KEY) {
129
- throw new Error("YTR_API_KEY is not set");
148
+ if (!yetter.apiKey) {
149
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
130
150
  }
131
151
  const client = new YetterImageClient({
132
- apiKey: process.env.YTR_API_KEY,
152
+ apiKey: yetter.apiKey,
153
+ endpoint: yetter.endpoint
133
154
  });
134
155
 
135
156
  const endpoint = client.getApiEndpoint();
@@ -149,14 +170,15 @@ export class yetter {
149
170
  model: string,
150
171
  options: StreamOptions
151
172
  ): Promise<YetterStream> {
152
- if (!process.env.YTR_API_KEY) {
153
- throw new Error("YTR_API_KEY is not set");
173
+ if (!yetter.apiKey) {
174
+ throw new Error("API key is not configured. Call yetter.configure({ apiKey: 'your_key' }) or set YTR_API_KEY.");
154
175
  }
155
- const apiClient = new YetterImageClient({
156
- apiKey: process.env.YTR_API_KEY,
176
+ const client = new YetterImageClient({
177
+ apiKey: yetter.apiKey,
178
+ endpoint: yetter.endpoint
157
179
  });
158
180
 
159
- const initialApiResponse = await apiClient.generateImage({
181
+ const initialApiResponse = await client.generateImage({
160
182
  model: model,
161
183
  ...options.input,
162
184
  });
@@ -164,7 +186,7 @@ export class yetter {
164
186
  const requestId = initialApiResponse.request_id;
165
187
  const responseUrl = initialApiResponse.response_url;
166
188
  const cancelUrl = initialApiResponse.cancel_url;
167
- const sseStreamUrl = `${apiClient.getApiEndpoint()}/${model}/requests/${requestId}/status/stream`;
189
+ const sseStreamUrl = `${client.getApiEndpoint()}/${model}/requests/${requestId}/status/stream`;
168
190
 
169
191
  let eventSource: EventSource;
170
192
  let streamEnded = false;
@@ -193,7 +215,7 @@ export class yetter {
193
215
  // Check for terminal events to resolve/reject the donePromise
194
216
  if (event.status === "COMPLETED") {
195
217
  streamEnded = true;
196
- apiClient.getResponse({ url: responseUrl })
218
+ client.getResponse({ url: responseUrl })
197
219
  .then(resolveDonePromise)
198
220
  .catch(rejectDonePromise)
199
221
  .finally(() => this.close());
@@ -249,7 +271,7 @@ export class yetter {
249
271
  };
250
272
 
251
273
  eventSource = new EventSourcePolyfill(sseStreamUrl, {
252
- headers: { 'Authorization': `${process.env.YTR_API_KEY}` }
274
+ headers: { 'Authorization': `${yetter.apiKey}` }
253
275
  } as any);
254
276
 
255
277
  eventSource.onopen = (event: Event) => {
@@ -296,7 +318,7 @@ export class yetter {
296
318
  cancel: async () => {
297
319
  controller.close();
298
320
  try {
299
- await apiClient.cancel({ url: cancelUrl });
321
+ await client.cancel({ url: cancelUrl });
300
322
  console.log(`Stream for ${requestId} - underlying request cancelled.`);
301
323
  } catch (e: any) {
302
324
  console.error(`Error cancelling underlying request for stream ${requestId}:`, e.message);
package/src/types.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface ClientOptions {
2
2
  apiKey: string;
3
3
  endpoint?: string;
4
+ is_bearer?: boolean;
4
5
  }
5
6
 
6
7
  export interface GenerateImageRequest {
package/stress.ts DELETED
@@ -1,135 +0,0 @@
1
- import { yetter } from "../src/client.js";
2
-
3
- const CONCURRENCY_LEVEL = 5; // Number of streams to run in parallel
4
- const MODEL_NAME = "ytr-ai/flux/dev"; // Model to use for streaming
5
-
6
- let streamCounter = 0;
7
- const activePromises: Promise<void>[] = [];
8
- let shuttingDown = false;
9
-
10
- /**
11
- * Runs a single stream, measures latency, and logs results.
12
- * @param id - A unique identifier for the stream.
13
- */
14
- async function runStream(id: number): Promise<void> {
15
- const startTime = Date.now();
16
- let streamRequestId = "";
17
-
18
- console.log(`[${new Date().toLocaleTimeString()}] [Stream ${id}] Starting...`);
19
-
20
- try {
21
- const streamInstance = await yetter.stream(MODEL_NAME, {
22
- input: {
23
- prompt: `A beautiful landscape painting, style of Van Gogh, stream ${id}`,
24
- // You can add other parameters like seed or num_inference_steps here
25
- },
26
- });
27
- streamRequestId = streamInstance.getRequestId();
28
- console.log(`[${new Date().toLocaleTimeString()}] [Stream ${id}] Initiated. Request ID: ${streamRequestId}`);
29
-
30
- // We are primarily interested in the latency of done(), so event iteration can be minimal or skipped.
31
- // for await (const event of streamInstance) {
32
- // console.log(`[${new Date().toLocaleTimeString()}][STREAM EVENT - ${streamRequestId}] Status: ${event.status}, QPos: ${event.queue_position}`);
33
- // }
34
-
35
- // Wait for the final result from the done() method
36
- await streamInstance.done();
37
- const endTime = Date.now();
38
- const latency = endTime - startTime;
39
- console.log(`[${new Date().toLocaleTimeString()}] [Stream ${id}] Finished. Request ID: ${streamRequestId}. Latency: ${latency}ms`);
40
-
41
- } catch (err: any) {
42
- const endTime = Date.now();
43
- const latency = endTime - startTime; // Still record latency up to the point of failure
44
- console.error(`[${new Date().toLocaleTimeString()}] [Stream ${id}] Failed. Request ID: ${streamRequestId || 'UNKNOWN'}. Latency: ${latency}ms. Error: ${err.message || err}`);
45
- // Optionally, rethrow or handle more specifically if needed
46
- }
47
- }
48
-
49
- /**
50
- * Launches a new stream if concurrency limit is not reached and not shutting down.
51
- * Manages the activePromises array.
52
- */
53
- async function launchStreamIfNotBusy(): Promise<void> {
54
- if (shuttingDown || activePromises.length >= CONCURRENCY_LEVEL) {
55
- return;
56
- }
57
-
58
- streamCounter++;
59
- const currentStreamId = streamCounter;
60
-
61
- console.log(`[${new Date().toLocaleTimeString()}] Launching stream ${currentStreamId}. Active: ${activePromises.length}/${CONCURRENCY_LEVEL}`);
62
-
63
- const streamTask = runStream(currentStreamId);
64
- activePromises.push(streamTask);
65
-
66
- try {
67
- await streamTask;
68
- } catch (e) {
69
- // Errors are logged within runStream
70
- } finally {
71
- const index = activePromises.indexOf(streamTask);
72
- if (index > -1) {
73
- activePromises.splice(index, 1);
74
- }
75
- console.log(`[${new Date().toLocaleTimeString()}] Stream ${currentStreamId} completed. Active: ${activePromises.length}/${CONCURRENCY_LEVEL}`);
76
- // After a stream finishes, try to fill its slot immediately
77
- if (!shuttingDown) {
78
- launchStreamIfNotBusy(); // Non-blocking call to fill the slot
79
- }
80
- }
81
- }
82
-
83
- /**
84
- * Fills available slots up to the CONCURRENCY_LEVEL.
85
- */
86
- function fillSlots() {
87
- while (!shuttingDown && activePromises.length < CONCURRENCY_LEVEL) {
88
- // launchStreamIfNotBusy is async but we don't await it here
89
- // as we want to launch multiple streams in parallel.
90
- // It handles its own addition to activePromises.
91
- launchStreamIfNotBusy();
92
- }
93
- }
94
-
95
- /**
96
- * Main function to run the stress test.
97
- */
98
- async function mainStressTest() {
99
- console.log("--- Starting Yetter Stream Stress Test ---");
100
- console.log(`Concurrency Level (N): ${CONCURRENCY_LEVEL}`);
101
- console.log(`Target Model: ${MODEL_NAME}`);
102
- console.log("Press Ctrl+C to stop gracefully (will finish active streams).");
103
-
104
- process.on('SIGINT', async () => {
105
- console.log('\nSIGINT received. Gracefully shutting down...');
106
- shuttingDown = true;
107
- console.log(`No new streams will be launched. Waiting for ${activePromises.length} active stream(s) to complete...`);
108
- // The main loop will exit once activePromises is empty after shuttingDown is true.
109
- });
110
-
111
- // Initial fill
112
- fillSlots();
113
-
114
- // Keep the script running and responsive to shutdown, also periodically check to fill slots
115
- // in case some mechanism external to stream completion is needed (though launchStreamIfNotBusy's finally block should cover it)
116
- while (true) {
117
- if (shuttingDown && activePromises.length === 0) {
118
- break; // Exit condition: shutting down and all streams are done.
119
- }
120
- // Periodically try to fill slots, mainly as a fallback or if initial fills didn't max out.
121
- // The primary mechanism for refilling is within launchStreamIfNotBusy's finally block.
122
- if (!shuttingDown) {
123
- fillSlots();
124
- }
125
- await new Promise(resolve => setTimeout(resolve, 200)); // Interval for the main loop check
126
- }
127
-
128
- console.log('--- Stress Test Finished ---');
129
- console.log(`Total streams launched during the session: ${streamCounter}`);
130
- }
131
-
132
- mainStressTest().catch(err => {
133
- console.error("Unhandled error in mainStressTest:", err);
134
- process.exit(1);
135
- });