diff --git a/mateclaw-server/src/main/java/vip/mate/tool/image/provider/OpenAiImageProvider.java b/mateclaw-server/src/main/java/vip/mate/tool/image/provider/OpenAiImageProvider.java index 21e46bc9..151f53b1 100644 --- a/mateclaw-server/src/main/java/vip/mate/tool/image/provider/OpenAiImageProvider.java +++ b/mateclaw-server/src/main/java/vip/mate/tool/image/provider/OpenAiImageProvider.java @@ -17,11 +17,17 @@ import java.util.List; import java.util.Set; /** - * OpenAI 图片生成 Provider — 支持 DALL-E 3 / DALL-E 2 / gpt-image-1 + * OpenAI 图片生成 Provider + * — 支持 DALL-E 3 / DALL-E 2 / gpt-image-1 / gpt-image-2 (low/medium/high) *
- * 同步模式:直接返回图片 URL。 + * 同步模式:返回图片 URL(DALL-E 系列)或 base64 data URL(gpt-image-2 系列)。 * 复用已有的 OpenAI LLM provider 的 API Key。 * + *
gpt-image-2 三档质量做成 3 个虚拟 model ID(参考 hermes-agent
+ * plugins/image_gen/openai/__init__.py 的 model catalog 设计),让 picker
+ * 能直接选 fast/balanced/high。三档底层都打到 API model {@code "gpt-image-2"},
+ * 区别仅在 {@code quality} 参数。
+ *
* @author MateClaw Team
*/
@Slf4j
@@ -34,6 +40,20 @@ public class OpenAiImageProvider implements ImageGenerationProvider {
private static final String DEFAULT_MODEL = "dall-e-3";
+ /**
+ * gpt-image-2 真实 API model 名。三档虚拟 ID(gpt-image-2-low/medium/high)
+ * 在 submit 时全部打到这个 API model + 不同 quality 参数。
+ */
+ private static final String GPT_IMAGE_2_API_MODEL = "gpt-image-2";
+
+ /** gpt-image-2 系列虚拟 ID 列表 — 用于 capabilities 与分支判定。 */
+ private static final List Null-safe: {@code List.of(...).contains(null)} throws NPE, which we
+ * pre-empt with an explicit null check. */
+ static boolean isGptImage2Tier(String modelId) {
+ return modelId != null && GPT_IMAGE_2_TIERS.contains(modelId);
+ }
+
private String getOpenAiApiKey() {
try {
var providerEntity = modelProviderService.getProviderConfig("openai");
@@ -154,14 +232,34 @@ public class OpenAiImageProvider implements ImageGenerationProvider {
}
}
- private String normalizeSize(String size, String aspectRatio) {
- // 优先使用 size
- if (size != null && !size.isBlank()) {
- List 渲染,沿用
+ // GoogleImagenProvider / MiniMaxImageProvider 的现成模式。
+ String b64 = item.has("b64_json") ? item.get("b64_json").asText() : null;
+ if (b64 != null && !b64.isBlank()) {
+ imageUrls.add("data:image/png;base64," + b64);
+ }
+ } else {
+ String imageUrl = item.has("url") ? item.get("url").asText() : null;
+ if (imageUrl != null) {
+ imageUrls.add(imageUrl);
+ }
}
}
if (imageUrls.isEmpty()) {
- return ImageSubmitResult.failure(id(), "API 返回成功但未包含图片 URL");
+ return ImageSubmitResult.failure(id(),
+ isGptImage2 ? "API 返回成功但未包含 b64_json 图片数据"
+ : "API 返回成功但未包含图片 URL");
}
- log.info("[OpenAI Image] Generated {} image(s) (model={})", imageUrls.size(), model);
+ log.info("[OpenAI Image] Generated {} image(s) (model={})",
+ imageUrls.size(), requestedModel);
return ImageSubmitResult.syncSuccess(id(), imageUrls);
} else {
String errMsg = result.has("error")
@@ -136,6 +196,24 @@ public class OpenAiImageProvider implements ImageGenerationProvider {
}
}
+ /** Map gpt-image-2-{low|medium|high} → quality string sent to API.
+ * Package-private + static for unit testability. */
+ static String qualityForTier(String tierModelId) {
+ return switch (tierModelId) {
+ case "gpt-image-2-low" -> "low";
+ case "gpt-image-2-high" -> "high";
+ default -> "medium"; // gpt-image-2-medium + 任何未来 tier 都默认 medium
+ };
+ }
+
+ /** Returns true if the given model id is a gpt-image-2 virtual tier.
+ * Package-private + static for unit testability.
+ *
+ *
+ */
+ /** Package-private + static-ish for unit testability. Kept instance-method to
+ * stay close to the call site — no instance state is touched. */
+ String normalizeSize(String size, String aspectRatio, boolean isGptImage2) {
+ List