diff --git a/relay/channel/gemini/image.go b/relay/channel/gemini/image.go index 4e19048..017319e 100644 --- a/relay/channel/gemini/image.go +++ b/relay/channel/gemini/image.go @@ -20,6 +20,11 @@ func convertImageRequestToChat(requestBody []byte, info *common.RelayInfo) (io.R return nil, fmt.Errorf("parse image request: %w", err) } + // Banana 内生图模式不支持 N>1,每次请求只生成一张图 + if imgReq.N != nil && *imgReq.N > 1 { + return nil, constant.NewRequestError("Gemini native image generation (Banana mode) does not support n > 1; use Imagen models (e.g. imagen-3.0-generate-002) or call the endpoint multiple times", nil) + } + generationConfig := &dto.GeminiGenerationConfig{ ResponseModalities: []string{"TEXT", "IMAGE"}, } diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 2a8a3b0..2e217f1 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -26,14 +26,17 @@ const ( ) // GetTimeoutSeconds 返回请求超时秒数。 -// 渠道自定义优先,未配置时根据请求模式返回默认值(图片生成 600s,其余 60s)。 +// 图片生成模式强制最低 600s(即使渠道配置了更短的自定义超时),其余模式渠道自定义优先。 func (s ChannelSettings) GetTimeoutSeconds(relayMode int) int { + if constant.RelayMode(relayMode) == constant.RelayModeImagesGenerations { + if s.TimeoutSeconds > ImagesGenerationTimeoutSecs { + return s.TimeoutSeconds + } + return ImagesGenerationTimeoutSecs + } if s.TimeoutSeconds > 0 { return s.TimeoutSeconds } - if constant.RelayMode(relayMode) == constant.RelayModeImagesGenerations { - return ImagesGenerationTimeoutSecs - } return DefaultTimeoutSeconds } diff --git a/web/tenant/src/views/service/playground/ImageTab.vue b/web/tenant/src/views/service/playground/ImageTab.vue index 924dc6e..59e4586 100644 --- a/web/tenant/src/views/service/playground/ImageTab.vue +++ b/web/tenant/src/views/service/playground/ImageTab.vue @@ -113,11 +113,26 @@ function parseValue(raw: string): string | number | boolean { return raw } -// 切换模型时自动应用预设 +// 默认参数项(无预设时显示,用户自行填写值) +const DEFAULT_PARAMS = [ + { key: 'size', value: '' }, + { key: 'quality', value: '' }, +] + +function applyDefaults() { + customParams.value = [] + for (const { key, value } of DEFAULT_PARAMS) { + addParam(key, value) + } +} + +// 切换模型时自动应用预设,无预设则显示默认参数项 watch(selectedModel, () => { const preset = currentPreset.value if (preset) { applyPreset() + } else { + applyDefaults() } }, { immediate: true }) @@ -225,13 +240,6 @@ function downloadImage(img: ImageResult, idx: number) { > 重置预设 - @@ -239,7 +247,7 @@ function downloadImage(img: ImageResult, idx: number) {
diff --git a/web/tenant/src/views/service/playground/ParamRefModal.vue b/web/tenant/src/views/service/playground/ParamRefModal.vue index d43d1ce..739a868 100644 --- a/web/tenant/src/views/service/playground/ParamRefModal.vue +++ b/web/tenant/src/views/service/playground/ParamRefModal.vue @@ -15,6 +15,7 @@ export interface ParamDef { export interface ModelParamGroup { key: string label: string + docUrl?: string params: ParamDef[] } @@ -35,11 +36,13 @@ const imageGroups: ModelParamGroup[] = [ { key: 'gemini-image', label: 'Gemini', + docUrl: 'https://ai.google.dev/gemini-api/docs/image-generation?hl=zh-cn', params: [ { name: 'prompt', type: 'string', required: true, description: '图像描述文本,支持中英文,可描述画面内容、风格、构图等' }, - { name: 'model', type: 'string', required: true, description: '模型 ID,如 gemini-2.5-flash-image、gemini-3-pro-image-preview' }, - { name: 'n', type: 'integer', required: false, default: '1', values: ['1'], description: '生成图像数量,目前仅支持 1' }, - { name: 'size', type: 'string', required: false, default: '1024x1024', values: ['1024x1024', '1536x1024', '1024x1536'], description: '图像尺寸(宽x高)。支持正方形和横/纵向' }, + { name: 'model', type: 'string', required: true, description: '模型 ID。Banana 内生图:gemini-2.5-flash-image、gemini-3-pro-image-preview 等;Imagen:imagen-3.0-generate-002 等' }, + { name: 'n', type: 'integer', required: false, default: '1', values: ['1', '2', '3', '4'], description: '生成图像数量。Banana 模式(gemini-*-image)仅支持 1,传 N>1 会报错;Imagen 模式(imagen-*)支持 1-4' }, + { name: 'size', type: 'string', required: false, default: '1024x1024', values: ['1024x1024', '1536x1024', '1024x1536', '1280x720', '720x1280', '1152x864', '864x1152', '1344x576', '1:1', '16:9', '9:16', '3:2', '2:3', '4:3', '3:4', '21:9'], description: '图像尺寸。支持 OpenAI 格式(宽x高)和 Gemini 原生比例格式(如 1:1、16:9)' }, + { name: 'quality', type: 'string', required: false, default: '1K', values: ['hd', 'high', 'standard', 'medium', 'low', '256', '512', '1K', '2K', '4K'], description: '图像分辨率/质量。OpenAI 格式 hd/high → 2K;Gemini 原生格式 256/512/1K/2K/4K 直接控制输出分辨率' }, { name: 'response_format', type: 'string', required: false, default: 'b64_json', values: ['b64_json', 'url'], description: '返回格式。b64_json 返回 Base64 编码;url 返回临时下载链接' }, ], }, @@ -165,6 +168,22 @@ const currentGroup = computed(() => groups.value.find(g => g.key === activeGroup