diff --git a/src/renderer/src/pages/paintings/AihubmixPage.tsx b/src/renderer/src/pages/paintings/AihubmixPage.tsx index 507554f1c..e9fea1269 100644 --- a/src/renderer/src/pages/paintings/AihubmixPage.tsx +++ b/src/renderer/src/pages/paintings/AihubmixPage.tsx @@ -93,7 +93,7 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => { const getNewPainting = useCallback(() => { return { ...DEFAULT_PAINTING, - model: mode === 'aihubmix_image_generate' ? 'gpt-image-1' : 'V_3', + model: mode === 'aihubmix_image_generate' ? 'gemini-3-pro-image-preview' : 'V_3', id: uuid() } }, [mode]) @@ -201,6 +201,74 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => { updatePaintingState({ files: validFiles, urls: validFiles.map((file) => file.name) }) } return + } else if (painting.model === 'gemini-3-pro-image-preview') { + const geminiUrl = `${aihubmixProvider.apiHost}/gemini/v1beta/models/gemini-3-pro-image-preview:streamGenerateContent` + const geminiHeaders = { + 'Content-Type': 'application/json', + 'x-goog-api-key': aihubmixProvider.apiKey + } + + const requestBody = { + contents: [ + { + parts: [ + { + text: prompt + } + ], + role: 'user' + } + ], + generationConfig: { + responseModalities: ['TEXT', 'IMAGE'], + imageConfig: { + aspectRatio: painting.aspectRatio?.replace('ASPECT_', '').replace('_', ':') || '1:1', + imageSize: painting.imageSize || '1k' + } + } + } + + logger.silly(`Gemini Request: ${JSON.stringify(requestBody)}`) + + const response = await fetch(geminiUrl, { + method: 'POST', + headers: geminiHeaders, + body: JSON.stringify(requestBody) + }) + + if (!response.ok) { + const errorData = await response.json() + logger.error('Gemini API Error:', errorData) + throw new Error(errorData.error?.message || '生成图像失败') + } + + const data = await response.json() + logger.silly(`Gemini API Response: ${JSON.stringify(data)}`) + + // Handle array response (stream) or single object + const responseItems = Array.isArray(data) ? data : [data] + const base64s: string[] = [] + + responseItems.forEach((item) => { + item.candidates?.forEach((candidate: any) => { + candidate.content?.parts?.forEach((part: any) => { + if (part.inlineData?.data) { + base64s.push(part.inlineData.data) + } + }) + }) + }) + + if (base64s.length > 0) { + const validFiles = await Promise.all( + base64s.map(async (base64: string) => { + return await window.api.file.saveBase64Image(base64) + }) + ) + await FileManager.addFiles(validFiles) + updatePaintingState({ files: validFiles, urls: validFiles.map((file) => file.name) }) + } + return } else if (painting.model === 'V_3') { // V3 API uses different endpoint and parameters format const formData = new FormData() diff --git a/src/renderer/src/pages/paintings/config/aihubmixConfig.tsx b/src/renderer/src/pages/paintings/config/aihubmixConfig.tsx index 97c57e5db..a203f9368 100644 --- a/src/renderer/src/pages/paintings/config/aihubmixConfig.tsx +++ b/src/renderer/src/pages/paintings/config/aihubmixConfig.tsx @@ -72,6 +72,7 @@ export const createModeConfigs = (): Record => { label: 'Gemini', title: 'Gemini', options: [ + { label: 'Nano Banana Pro', value: 'gemini-3-pro-image-preview' }, { label: 'imagen-4.0-preview', value: 'imagen-4.0-generate-preview-06-06' }, { label: 'imagen-4.0-ultra', value: 'imagen-4.0-ultra-generate-preview-06-06' } ] @@ -224,7 +225,20 @@ export const createModeConfigs = (): Record => { { label: '16:9', value: 'ASPECT_16_9' } ], initialValue: 'ASPECT_1_1', - condition: (painting) => Boolean(painting.model?.startsWith('imagen-')) + condition: (painting) => + Boolean(painting.model?.startsWith('imagen-') || painting.model === 'gemini-3-pro-image-preview') + }, + { + type: 'select', + key: 'imageSize', + title: 'paintings.image.size', + options: [ + { label: '1K', value: '1K' }, + { label: '2K', value: '2K' }, + { label: '4K', value: '4K' } + ], + initialValue: '1K', + condition: (painting) => painting.model === 'gemini-3-pro-image-preview' }, { type: 'select', @@ -398,7 +412,7 @@ export const createModeConfigs = (): Record => { // 几种默认的绘画配置 export const DEFAULT_PAINTING: PaintingAction = { id: 'aihubmix_1', - model: 'gpt-image-1', + model: 'gemini-3-pro-image-preview', aspectRatio: 'ASPECT_1_1', numImages: 1, styleType: 'AUTO', @@ -420,5 +434,6 @@ export const DEFAULT_PAINTING: PaintingAction = { moderation: 'auto', n: 1, numberOfImages: 4, - safetyTolerance: 6 + safetyTolerance: 6, + imageSize: '1K' } diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index f82fec8f0..bff57185a 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -320,6 +320,7 @@ export interface GeneratePainting extends PaintingParams { safetyTolerance?: number width?: number height?: number + imageSize?: string } export interface EditPainting extends PaintingParams {