MCPcopy
hub / github.com/garrytan/gstack / generateVariant

Function generateVariant

design/src/variants.ts:38–135  ·  view source on GitHub ↗
(
  apiKey: string,
  prompt: string,
  outputPath: string,
  size: string,
  quality: string,
  fetchFn: typeof globalThis.fetch = globalThis.fetch,
)

Source from the content-addressed store, hash-verified

36 * production code uses the global fetch by default.
37 */
38export async function generateVariant(
39 apiKey: string,
40 prompt: string,
41 outputPath: string,
42 size: string,
43 quality: string,
44 fetchFn: typeof globalThis.fetch = globalThis.fetch,
45): Promise<{ path: string; success: boolean; error?: string }> {
46 const maxRetries = 3;
47 const MAX_RETRY_AFTER_MS = 60_000; // cap honored Retry-After to bound stalls
48 let lastError = "";
49 let skipLeadingDelay = false;
50
51 for (let attempt = 0; attempt <= maxRetries; attempt++) {
52 if (attempt > 0 && !skipLeadingDelay) {
53 // Exponential backoff: 2s, 4s, 8s
54 const delay = Math.pow(2, attempt) * 1000;
55 console.error(` Rate limited, retrying in ${delay / 1000}s...`);
56 await new Promise(r => setTimeout(r, delay));
57 }
58 skipLeadingDelay = false;
59
60 const controller = new AbortController();
61 const timeout = setTimeout(() => controller.abort(), 240_000);
62
63 try {
64 const response = await fetchFn("https://api.openai.com/v1/responses", {
65 method: "POST",
66 headers: {
67 "Authorization": `Bearer ${apiKey}`,
68 "Content-Type": "application/json",
69 },
70 body: JSON.stringify({
71 model: "gpt-4o",
72 input: prompt,
73 tools: [{ type: "image_generation", model: "gpt-image-2", size, quality }],
74 }),
75 signal: controller.signal,
76 });
77
78 clearTimeout(timeout);
79
80 if (response.status === 429) {
81 lastError = "Rate limited (429)";
82 const retryAfter = response.headers.get("retry-after");
83 if (retryAfter) {
84 const trimmed = retryAfter.trim();
85 let waitMs: number | null = null;
86 if (/^\d+$/.test(trimmed)) {
87 // delta-seconds (RFC 7231)
88 waitMs = Math.min(Number.parseInt(trimmed, 10) * 1000, MAX_RETRY_AFTER_MS);
89 } else {
90 // HTTP-date (RFC 7231)
91 const dateMs = Date.parse(trimmed);
92 if (!Number.isNaN(dateMs)) {
93 waitMs = Math.min(Math.max(0, dateMs - Date.now()), MAX_RETRY_AFTER_MS);
94 }
95 }

Callers 3

variantsFunction · 0.85

Calls 2

getMethod · 0.45
textMethod · 0.45

Tested by

no test coverage detected