← back to Wallco Ai
scripts/dalle_compare.js
96 lines
'use strict';
/**
* Column 6 of the refiner comparison — OpenAI image generation, 1024×1024.
*
* Defaults to `gpt-image-1` (OpenAI's current image model, ~$0.04/image at
* high quality 1024×1024). The sk-proj key we route doesn't expose dall-e-3,
* and gpt-image-1 is the state-of-the-art replacement anyway.
*
* Same 25 prompts as refiner_compare.js. gpt-image-1 doesn't accept a seed,
* and the prompt-rewriter behavior differs by model. We pass the explicit
* "I NEED to test … AS-IS" preamble to discourage automatic rewriting.
*
* node scripts/dalle_compare.js
*/
require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') });
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const { FASHION_PALETTES, paletteString } = require('./fashion_palettes');
const { STYLES, OUT, haveImg, save } = require('./compare_progress');
const TAG = '[dalle]';
// gpt-image-1 = OpenAI's current image model (DALL-E 3's replacement).
// Set OPENAI_IMAGE_MODEL=dall-e-3 to override if your key has dall-e-3 access.
const MODEL = process.env.OPENAI_IMAGE_MODEL || 'gpt-image-1';
function loadKey() { return process.env.OPENAI_API_KEY || null; }
function buildPrompt(style, pal) {
// "I NEED to test" is the OpenAI-documented preamble that suppresses
// DALL-E 3's automatic prompt rewriting — we want the exact prompt to
// hit the model so column-vs-column is a fair comparison.
return 'I NEED to test how the model tokenizes this exact request. ' +
'DO NOT add any detail, just use it AS-IS: ' +
`A seamless wallpaper pattern, ${style} motif, ${paletteString(pal)}, ` +
'seamless tile, repeating pattern, no edges, fabric pattern, wallpaper design, archival quality, high detail, ' +
'screen-printed wallpaper, solid opaque flat ink, every motif fully filled with solid color, ' +
'no halftone, no dot screens, no gradients, no text, no watermark, no border.';
}
function genDalle(prompt, outPath) {
const KEY = loadKey();
if (!KEY) throw new Error('OPENAI_API_KEY not set in wallco-ai/.env');
const sh = (cmd, opts) => {
try { return execSync(cmd, opts); }
catch (e) { throw new Error(String((e && e.message) || e).split(KEY).join('sk-***REDACTED')); }
};
// gpt-image-1 always returns b64 (no response_format needed) and has a
// 4-tier quality enum (low/medium/high/auto). For dall-e-3, override:
// OPENAI_IMAGE_MODEL=dall-e-3 (uses standard/hd quality)
const isGpt = MODEL.startsWith('gpt-image');
const body = {
model: MODEL,
prompt,
n: 1,
size: '1024x1024',
quality: isGpt ? 'high' : 'hd',
...(isGpt ? {} : { response_format: 'b64_json' }),
};
const raw = sh(
`curl -sf -m 120 -H 'Authorization: Bearer ${KEY}' -H 'Content-Type: application/json' -X POST 'https://api.openai.com/v1/images/generations' -d @-`,
{ input: JSON.stringify(body), encoding: 'utf8', maxBuffer: 40 * 1024 * 1024 },
);
let res;
try { res = JSON.parse(raw); }
catch { throw new Error(`DALL-E non-JSON response: ${raw.slice(0, 300)}`); }
if (res.error) throw new Error(`DALL-E error: ${res.error.message || JSON.stringify(res.error)}`);
const b64 = res.data && res.data[0] && res.data[0].b64_json;
if (!b64) throw new Error(`DALL-E returned no image: ${JSON.stringify(res).slice(0, 300)}`);
fs.writeFileSync(outPath, Buffer.from(b64, 'base64'));
if (!haveImg(outPath)) throw new Error('DALL-E wrote a sub-1KB file (likely empty)');
return outPath;
}
(async () => {
const t0 = Date.now();
let ok = 0, fail = 0, skip = 0;
console.log(`${TAG} 25 renders via OpenAI ${MODEL} (HD)`);
for (let i = 0; i < STYLES.length; i++) {
const style = STYLES[i], pal = FASHION_PALETTES[i % FASHION_PALETTES.length];
const outPath = path.join(OUT, `${i}_dalle.png`);
if (haveImg(outPath)) { skip++; save(); console.log(` ${i} dalle — skip`); continue; }
try { genDalle(buildPrompt(style, pal), outPath); ok++; }
catch (e) { fail++; console.log(` ${i} dalle ERR ${e.message.slice(0, 200)}`); }
save();
console.log(`${TAG} [${i + 1}/25] ${style} · ${pal.brand} — ${haveImg(outPath) ? 'ok' : 'FAIL'} · ${((Date.now() - t0) / 60000).toFixed(1)}min`);
}
save();
console.log(`${TAG} DONE — ${ok} rendered, ${skip} skipped, ${fail} failed · ${((Date.now() - t0) / 60000).toFixed(1)}min`);
})().catch(e => { console.error(`${TAG} FATAL`, e.message); process.exit(1); });