@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.
Files changed (2) hide show
  1. package/index.mjs +32 -3
  2. 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
- if (err?.status !== 400) return false;
599
- return err?.param === 'background' || /transparent background is not supported/i.test(err?.message || '');
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 (or a fully opaque image): pad the original instead.
13690
+ // Nothing to trim: pad the original instead.
13662
13691
  }
13663
13692
 
13664
13693
  const inner = Math.max(1, Math.round(canvas * scale));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/ai_module",
3
- "version": "1.1.5654",
3
+ "version": "1.1.5655",
4
4
  "description": "Xuda AI Module",
5
5
  "main": "index.mjs",
6
6
  "type": "module",