mcp-duojie-image 0.5.3 → 0.5.5
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/dist/provider.js +50 -7
- package/package.json +1 -1
package/dist/provider.js
CHANGED
|
@@ -46,18 +46,57 @@ function mapSize(size) {
|
|
|
46
46
|
function isOpenAIImageModel(model) {
|
|
47
47
|
return model === "gpt-image-2";
|
|
48
48
|
}
|
|
49
|
+
function normalizeAspectRatio(aspectRatio) {
|
|
50
|
+
const normalized = aspectRatio?.trim().replace(":", ":").replace(/\s+/g, "");
|
|
51
|
+
const match = normalized?.match(/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)$/);
|
|
52
|
+
if (!match)
|
|
53
|
+
return undefined;
|
|
54
|
+
const width = Number(match[1]);
|
|
55
|
+
const height = Number(match[2]);
|
|
56
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
return `${match[1]}:${match[2]}`;
|
|
60
|
+
}
|
|
61
|
+
function parseAspectRatio(aspectRatio) {
|
|
62
|
+
const normalized = normalizeAspectRatio(aspectRatio);
|
|
63
|
+
const match = normalized?.match(/^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)$/);
|
|
64
|
+
if (!match)
|
|
65
|
+
return [1, 1];
|
|
66
|
+
return [Number(match[1]), Number(match[2])];
|
|
67
|
+
}
|
|
68
|
+
function mapOpenAI4KSize(aspectRatio) {
|
|
69
|
+
const [ratioWidth, ratioHeight] = parseAspectRatio(aspectRatio);
|
|
70
|
+
const maxPixels = 3840 * 2160;
|
|
71
|
+
const maxEdge = 3840;
|
|
72
|
+
let width = Math.floor(Math.sqrt((maxPixels * ratioWidth) / ratioHeight));
|
|
73
|
+
let height = Math.floor((width * ratioHeight) / ratioWidth);
|
|
74
|
+
if (width > maxEdge) {
|
|
75
|
+
width = maxEdge;
|
|
76
|
+
height = Math.floor((width * ratioHeight) / ratioWidth);
|
|
77
|
+
}
|
|
78
|
+
if (height > maxEdge) {
|
|
79
|
+
height = maxEdge;
|
|
80
|
+
width = Math.floor((height * ratioWidth) / ratioHeight);
|
|
81
|
+
}
|
|
82
|
+
width = Math.floor(width / 8) * 8;
|
|
83
|
+
height = Math.floor(height / 8) * 8;
|
|
84
|
+
return `${width}x${height}`;
|
|
85
|
+
}
|
|
49
86
|
function mapOpenAISize(size, aspectRatio) {
|
|
50
87
|
const squareBySize = {
|
|
51
88
|
"512px": "512x512",
|
|
52
89
|
"1K": "1024x1024",
|
|
53
90
|
"2K": "2048x2048",
|
|
54
|
-
"4K": "4096x4096",
|
|
55
91
|
};
|
|
56
|
-
if (
|
|
57
|
-
return
|
|
92
|
+
if (size === "4K")
|
|
93
|
+
return mapOpenAI4KSize(aspectRatio);
|
|
94
|
+
const [ratioWidth, ratioHeight] = parseAspectRatio(aspectRatio);
|
|
95
|
+
if (ratioWidth > ratioHeight) {
|
|
96
|
+
return size === "2K" ? "2048x1536" : "1536x1024";
|
|
58
97
|
}
|
|
59
|
-
if (
|
|
60
|
-
return size === "2K" ? "1536x2048" :
|
|
98
|
+
if (ratioHeight > ratioWidth) {
|
|
99
|
+
return size === "2K" ? "1536x2048" : "1024x1536";
|
|
61
100
|
}
|
|
62
101
|
return squareBySize[size] ?? "1024x1024";
|
|
63
102
|
}
|
|
@@ -124,7 +163,8 @@ async function generateGeminiImage(config, opts) {
|
|
|
124
163
|
async function generateOpenAIImage(config, opts) {
|
|
125
164
|
const model = opts.model ?? config.model;
|
|
126
165
|
const prompt = opts.negativePrompt ? `${opts.prompt}\n\nAvoid: ${opts.negativePrompt}` : opts.prompt;
|
|
127
|
-
const
|
|
166
|
+
const aspectRatio = normalizeAspectRatio(opts.aspectRatio);
|
|
167
|
+
const size = mapOpenAISize(opts.size ?? "1K", aspectRatio);
|
|
128
168
|
const hasReferenceImages = Boolean(opts.referenceImages?.length);
|
|
129
169
|
const url = `${config.baseUrl}${hasReferenceImages ? "/v1/images/edits" : "/v1/images/generations"}`;
|
|
130
170
|
let body;
|
|
@@ -136,6 +176,9 @@ async function generateOpenAIImage(config, opts) {
|
|
|
136
176
|
form.set("model", model);
|
|
137
177
|
form.set("prompt", prompt);
|
|
138
178
|
form.set("size", size);
|
|
179
|
+
if (aspectRatio) {
|
|
180
|
+
form.set("aspect_ratio", aspectRatio);
|
|
181
|
+
}
|
|
139
182
|
for (const [index, imgPath] of opts.referenceImages.entries()) {
|
|
140
183
|
const data = await resizeIfNeeded(imgPath);
|
|
141
184
|
const bytes = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
@@ -145,7 +188,7 @@ async function generateOpenAIImage(config, opts) {
|
|
|
145
188
|
}
|
|
146
189
|
else {
|
|
147
190
|
headers = { ...headers, "Content-Type": "application/json" };
|
|
148
|
-
body = JSON.stringify({ model, prompt, size });
|
|
191
|
+
body = JSON.stringify({ model, prompt, size, ...(aspectRatio ? { aspect_ratio: aspectRatio } : {}) });
|
|
149
192
|
}
|
|
150
193
|
const res = await fetchWithRetry(url, {
|
|
151
194
|
method: "POST",
|