1
This commit is contained in:
@@ -14,7 +14,6 @@ const promptsPath = path.join(
|
||||
);
|
||||
const defaultOutDir = path.join(repoRoot, 'public', 'puzzle-creation-templates');
|
||||
const defaultTimeoutMs = 180000;
|
||||
const pollDelayMs = 3000;
|
||||
|
||||
const args = new Map();
|
||||
for (let index = 2; index < process.argv.length; index += 1) {
|
||||
@@ -66,15 +65,23 @@ function resolveEnv() {
|
||||
...process.env,
|
||||
};
|
||||
return {
|
||||
baseUrl: String(loaded.APIMART_BASE_URL || '').trim().replace(/\/+$/u, ''),
|
||||
apiKey: String(loaded.APIMART_API_KEY || '').trim(),
|
||||
baseUrl: String(loaded.VECTOR_ENGINE_BASE_URL || '')
|
||||
.trim()
|
||||
.replace(/\/+$/u, ''),
|
||||
apiKey: String(loaded.VECTOR_ENGINE_API_KEY || '').trim(),
|
||||
timeoutMs: Number.parseInt(
|
||||
String(loaded.APIMART_IMAGE_REQUEST_TIMEOUT_MS || defaultTimeoutMs),
|
||||
String(loaded.VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS || defaultTimeoutMs),
|
||||
10,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function buildVectorEngineImagesGenerationUrl(baseUrl) {
|
||||
return baseUrl.endsWith('/v1')
|
||||
? `${baseUrl}/images/generations`
|
||||
: `${baseUrl}/v1/images/generations`;
|
||||
}
|
||||
|
||||
function buildPrompt(template) {
|
||||
return [
|
||||
'请生成一张高清 1:1 方形插画,用作拼图创作模板样例图。',
|
||||
@@ -124,14 +131,6 @@ function extractBase64Images(payload) {
|
||||
return values;
|
||||
}
|
||||
|
||||
function extractTaskId(payload) {
|
||||
const ids = [];
|
||||
collectStringsByKey(payload, 'task_id', ids);
|
||||
collectStringsByKey(payload, 'taskId', ids);
|
||||
collectStringsByKey(payload, 'id', ids);
|
||||
return ids[0] || null;
|
||||
}
|
||||
|
||||
function inferExtensionFromContentType(contentType) {
|
||||
const normalized = contentType.split(';')[0]?.trim().toLowerCase();
|
||||
if (normalized === 'image/png') {
|
||||
@@ -172,7 +171,7 @@ async function fetchJson(url, options, timeoutMs) {
|
||||
});
|
||||
const text = await response.text();
|
||||
if (!response.ok) {
|
||||
throw new Error(`APIMart ${response.status}: ${text.slice(0, 600)}`);
|
||||
throw new Error(`VectorEngine ${response.status}: ${text.slice(0, 600)}`);
|
||||
}
|
||||
return JSON.parse(text);
|
||||
} finally {
|
||||
@@ -200,52 +199,20 @@ async function downloadUrl(url, timeoutMs) {
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForTask(env, taskId) {
|
||||
const deadline = Date.now() + env.timeoutMs;
|
||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
const payload = await fetchJson(
|
||||
`${env.baseUrl}/tasks/${encodeURIComponent(taskId)}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${env.apiKey}`,
|
||||
},
|
||||
},
|
||||
env.timeoutMs,
|
||||
);
|
||||
const statuses = [];
|
||||
collectStringsByKey(payload, 'status', statuses);
|
||||
collectStringsByKey(payload, 'task_status', statuses);
|
||||
const status = String(statuses[0] || '').trim().toLowerCase();
|
||||
|
||||
if (['completed', 'succeeded', 'success'].includes(status)) {
|
||||
return payload;
|
||||
}
|
||||
if (['failed', 'error', 'canceled', 'cancelled', 'unknown'].includes(status)) {
|
||||
throw new Error(`APIMart task ${taskId} failed: ${JSON.stringify(payload).slice(0, 600)}`);
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, pollDelayMs));
|
||||
}
|
||||
|
||||
throw new Error(`APIMart task ${taskId} timed out`);
|
||||
}
|
||||
|
||||
async function generateOne(env, template, outDir) {
|
||||
const requestBody = {
|
||||
model: 'gpt-image-2',
|
||||
model: 'gpt-image-2-all',
|
||||
prompt: buildPrompt(template),
|
||||
n: 1,
|
||||
official_fallback: true,
|
||||
size: '1:1',
|
||||
size: '1024x1024',
|
||||
};
|
||||
const payload = await fetchJson(
|
||||
`${env.baseUrl}/images/generations`,
|
||||
buildVectorEngineImagesGenerationUrl(env.baseUrl),
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${env.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
@@ -253,12 +220,8 @@ async function generateOne(env, template, outDir) {
|
||||
env.timeoutMs,
|
||||
);
|
||||
|
||||
const resolvedPayload =
|
||||
extractImageUrls(payload).length || extractBase64Images(payload).length
|
||||
? payload
|
||||
: await waitForTask(env, extractTaskId(payload));
|
||||
const urls = extractImageUrls(resolvedPayload);
|
||||
const b64Images = extractBase64Images(resolvedPayload);
|
||||
const urls = extractImageUrls(payload);
|
||||
const b64Images = extractBase64Images(payload);
|
||||
|
||||
let image;
|
||||
if (urls[0]) {
|
||||
@@ -270,7 +233,7 @@ async function generateOne(env, template, outDir) {
|
||||
extension: inferExtensionFromBytes(bytes),
|
||||
};
|
||||
} else {
|
||||
throw new Error(`APIMart returned no image for ${template.id}`);
|
||||
throw new Error(`VectorEngine returned no image for ${template.id}`);
|
||||
}
|
||||
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
@@ -302,11 +265,10 @@ if (dryRun) {
|
||||
id: template.id,
|
||||
title: template.title,
|
||||
body: {
|
||||
model: 'gpt-image-2',
|
||||
model: 'gpt-image-2-all',
|
||||
prompt: buildPrompt(template),
|
||||
n: 1,
|
||||
official_fallback: true,
|
||||
size: '1:1',
|
||||
size: '1024x1024',
|
||||
},
|
||||
})),
|
||||
},
|
||||
@@ -322,7 +284,7 @@ if (!env.baseUrl || !env.apiKey) {
|
||||
console.error(
|
||||
JSON.stringify({
|
||||
ok: false,
|
||||
error: 'Missing APIMART_BASE_URL or APIMART_API_KEY',
|
||||
error: 'Missing VECTOR_ENGINE_BASE_URL or VECTOR_ENGINE_API_KEY',
|
||||
hasBaseUrl: Boolean(env.baseUrl),
|
||||
hasApiKey: Boolean(env.apiKey),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user