@xuda.io/ai_module 1.1.5654 → 1.1.5655
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/index.mjs +32 -3
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -594,9 +594,17 @@ const transparent_image_model = function () {
|
|
|
594
594
|
return _conf?.transparent_image_model || 'img-1';
|
|
595
595
|
};
|
|
596
596
|
|
|
597
|
+
// UI-210: read the status and the param off whichever shape the error arrives in. The
|
|
598
|
+
// first pass only looked at `err.status` and `err.param`, which is the openai-node
|
|
599
|
+
// BadRequestError shape; the same refusal wrapped by a retry helper or an http client
|
|
600
|
+
// surfaces as `err.response.status` / `err.error.param` and slipped straight past the
|
|
601
|
+
// guard, so the call that was supposed to degrade threw instead. The message test is the
|
|
602
|
+
// backstop for anything that carries neither.
|
|
597
603
|
const _is_transparent_background_rejected = function (err) {
|
|
598
|
-
|
|
599
|
-
|
|
604
|
+
const status = err?.status ?? err?.statusCode ?? err?.response?.status;
|
|
605
|
+
if (status !== 400) return false;
|
|
606
|
+
const param = err?.param ?? err?.error?.param ?? err?.response?.data?.error?.param;
|
|
607
|
+
return param === 'background' || /transparent background is not supported/i.test(err?.message || err?.error?.message || '');
|
|
600
608
|
};
|
|
601
609
|
|
|
602
610
|
// images.edit asking for a transparent background, degrading to an opaque render if the
|
|
@@ -13654,11 +13662,32 @@ const pad_transparent_subject = async function (base64, { canvas = 1024, scale =
|
|
|
13654
13662
|
try {
|
|
13655
13663
|
const input = Buffer.from(base64, 'base64');
|
|
13656
13664
|
|
|
13665
|
+
// UI-210: an OPAQUE render must not be padded. edit_image_transparent degrades to an
|
|
13666
|
+
// opaque image whenever the model refuses `background: 'transparent'` (UI-138), and
|
|
13667
|
+
// that render is a full rectangle of artwork with its own backdrop. Compositing it onto
|
|
13668
|
+
// a transparent canvas would leave a hard-edged photo floating in the middle of the
|
|
13669
|
+
// card with see-through margins around it, which looks far worse than the edge-to-edge
|
|
13670
|
+
// framing this exists to fix. The padding only makes sense for a cutout.
|
|
13671
|
+
const meta_in = await sharp(input).metadata();
|
|
13672
|
+
if (!meta_in.hasAlpha) {
|
|
13673
|
+
console.log('[pad_transparent_subject] opaque render, leaving it edge to edge');
|
|
13674
|
+
return base64;
|
|
13675
|
+
}
|
|
13676
|
+
const { data: alpha, info: alpha_info } = await sharp(input).ensureAlpha().extractChannel('alpha').raw().toBuffer({ resolveWithObject: true });
|
|
13677
|
+
let clear = 0;
|
|
13678
|
+
for (let i = 0; i < alpha.length; i++) if (alpha[i] < 8) clear++;
|
|
13679
|
+
// A real cutout leaves a good share of the frame empty. Anything below this is a
|
|
13680
|
+
// near-opaque render with at most a stray soft edge, so treat it as opaque.
|
|
13681
|
+
if (clear / (alpha_info.width * alpha_info.height) < 0.05) {
|
|
13682
|
+
console.log('[pad_transparent_subject] no usable cutout, leaving it edge to edge');
|
|
13683
|
+
return base64;
|
|
13684
|
+
}
|
|
13685
|
+
|
|
13657
13686
|
let trimmed = input;
|
|
13658
13687
|
try {
|
|
13659
13688
|
trimmed = await sharp(input).trim().png().toBuffer();
|
|
13660
13689
|
} catch (err) {
|
|
13661
|
-
// Nothing to trim
|
|
13690
|
+
// Nothing to trim: pad the original instead.
|
|
13662
13691
|
}
|
|
13663
13692
|
|
|
13664
13693
|
const inner = Math.max(1, Math.round(canvas * scale));
|