From a54efb146a2c2e5a0c02507499b102efc84c5f88 Mon Sep 17 00:00:00 2001 From: suyx Date: Tue, 4 Aug 2026 18:38:33 +0800 Subject: [PATCH] feat(wp7-02): support Gemini chat image relay --- scripts/lib/wp7-02-controlled-executor.mjs | 46 ++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/scripts/lib/wp7-02-controlled-executor.mjs b/scripts/lib/wp7-02-controlled-executor.mjs index 1cfce51..8f9010f 100644 --- a/scripts/lib/wp7-02-controlled-executor.mjs +++ b/scripts/lib/wp7-02-controlled-executor.mjs @@ -26,7 +26,7 @@ function assertModelConfig(modelConfig) { const profile = modelConfig.route_profile; if (!profile || typeof profile !== "object" || typeof profile.endpoint !== "string" || !profile.endpoint.startsWith("https://oneapi.intelligrow.cn/") - || !["gemini-interactions-v1beta", "gemini-native-v1beta", "openai-images-v1"].includes(profile.protocol_version)) { + || !["gemini-interactions-v1beta", "gemini-native-v1beta", "gemini-openai-chat-v1", "openai-images-v1"].includes(profile.protocol_version)) { throw new Error("WP7_02_ROUTE_PROFILE_INVALID"); } if (profile.protocol_version === "gemini-interactions-v1beta" @@ -34,6 +34,11 @@ function assertModelConfig(modelConfig) { || profile.provider_model_id !== "gemini-3.1-flash-image")) { throw new Error("WP7_02_ROUTE_PROFILE_INVALID"); } + if (profile.protocol_version === "gemini-openai-chat-v1" + && (profile.endpoint !== "https://oneapi.intelligrow.cn/v1/chat/completions" + || profile.provider_model_id !== "gemini-3.1-flash-image")) { + throw new Error("WP7_02_ROUTE_PROFILE_INVALID"); + } if (profile.protocol_version === "openai-images-v1" && (profile.reference_endpoint !== "https://oneapi.intelligrow.cn/v1/images/edits")) { throw new Error("WP7_02_REFERENCE_ROUTE_PROFILE_INVALID"); @@ -108,6 +113,28 @@ export function buildProviderRequest({ modelConfig, prompt, ratio, reference }) url: config.route_profile.endpoint, }; } + if (config.route_profile.protocol_version === "gemini-openai-chat-v1") { + const content = reference + ? [ + { text: prompt, type: "text" }, + { + image_url: { url: `data:${reference.mime_type};base64,${reference.bytes.toString("base64")}` }, + type: "image_url", + }, + ] + : prompt; + return { + body: { + extra_body: { google: { image_config: { aspect_ratio: ratio, image_size: "1K" } } }, + messages: [{ content, role: "user" }], + model: config.route_profile.provider_model_id, + stream: false, + }, + headers, + method: "POST", + url: config.route_profile.endpoint, + }; + } const providerModelId = config.route_profile.provider_model_id ?? config.model_id; const body = { model: providerModelId, @@ -193,12 +220,20 @@ function geminiUsage(response) { function openAiUsage(response) { const usage = response?.usage; return { - input_units: integerOrZero(usage?.input_tokens ?? usage?.inputTokens), - output_units: integerOrZero(usage?.output_tokens ?? usage?.outputTokens), + input_units: integerOrZero(usage?.input_tokens ?? usage?.inputTokens ?? usage?.prompt_tokens ?? usage?.promptTokens), + output_units: integerOrZero(usage?.output_tokens ?? usage?.outputTokens ?? usage?.completion_tokens ?? usage?.completionTokens), total_units: integerOrZero(usage?.total_tokens ?? usage?.totalTokens), }; } +function openAiChatImage(response) { + const content = response?.choices?.[0]?.message?.content; + if (typeof content !== "string") throw new Error("WP7_02_RESPONSE_SINGLE_IMAGE_REQUIRED"); + const matches = [...content.matchAll(/!\[[^\]]*\]\(\s*data:(image\/(?:jpeg|png|webp));base64,([A-Za-z0-9+/=\r\n]+)\s*\)/gi)]; + if (matches.length !== 1) throw new Error("WP7_02_RESPONSE_SINGLE_IMAGE_REQUIRED"); + return { data: matches[0][2], mime: matches[0][1].toLowerCase() }; +} + function interactionUsage(response) { const usage = response?.usage; return { @@ -230,6 +265,11 @@ export function normalizeProviderResponse(modelConfig, response) { mime = images[0].mimeType ?? images[0].mime_type; bytes = Buffer.from(images[0].data, "base64"); usageSummary = geminiUsage(response); + } else if (config.route_profile.protocol_version === "gemini-openai-chat-v1") { + const image = openAiChatImage(response); + bytes = Buffer.from(image.data, "base64"); + mime = image.mime; + usageSummary = openAiUsage(response); } else { if (!Array.isArray(response?.data) || response.data.length !== 1 || typeof response.data[0]?.b64_json !== "string") { throw new Error("WP7_02_RESPONSE_SINGLE_IMAGE_REQUIRED");