/** * Seedream (ByteDance / Doubao / Ark) Image Generation Adapter * * Uses OpenAI-compatible synchronous API format. * Endpoint: https://ark.cn-beijing.volces.com/api/v3/images/generations * * Supported models: * - doubao-seedream-5-0-260128 (latest / Lite, text2img + img2img + multi-ref + group) * - doubao-seedream-4-5-251128 * - doubao-seedream-4-0-250828 * - doubao-seedream-3-0-t2i-250415 * * API docs: https://www.volcengine.com/docs/6791/1399028 */ import type { ImageGenerationConfig, ImageGenerationOptions, ImageGenerationResult, } from '../types'; const DEFAULT_MODEL = 'doubao-seedream-5-0-260128'; const DEFAULT_BASE_URL = 'https://ark.cn-beijing.volces.com'; /** * Map our aspect ratio + size to Seedream size format "WxH". * Seedream requires minimum 3,686,400 pixels total. * Common sizes: 2048x2048 (2K), 2560x1440 (16:9), 1920x1920. */ function resolveSeedreamSize(options: ImageGenerationOptions): string { if (options.width && options.height) { // Ensure minimum pixel count (3,686,400) const pixels = options.width * options.height; if (pixels < 3_686_400) { // Scale up proportionally const scale = Math.ceil(Math.sqrt(3_686_400 / pixels)); return `${options.width * scale}x${options.height * scale}`; } return `${options.width}x${options.height}`; } // Default to 2K for quality return '2K'; } /** * Lightweight connectivity test — validates API key by making a minimal * request that triggers auth check. 401/403 means key invalid. */ export async function testSeedreamConnectivity( config: ImageGenerationConfig, ): Promise<{ success: boolean; message: string }> { const baseUrl = config.baseUrl || DEFAULT_BASE_URL; try { // Send a request with empty prompt — auth failure (401/403) means bad key, // any other error (400) means key is valid but request is intentionally bad const response = await fetch(`${baseUrl}/api/v3/images/generations`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.apiKey}`, }, body: JSON.stringify({ model: config.model || DEFAULT_MODEL, prompt: '', size: '1x1', }), }); if (response.status === 401 || response.status === 403) { const text = await response.text(); return { success: false, message: `Seedream auth failed (${response.status}): ${text}`, }; } return { success: true, message: 'Connected to Seedream' }; } catch (err) { return { success: false, message: `Seedream connectivity error: ${err}` }; } } export async function generateWithSeedream( config: ImageGenerationConfig, options: ImageGenerationOptions, ): Promise { const baseUrl = config.baseUrl || DEFAULT_BASE_URL; const response = await fetch(`${baseUrl}/api/v3/images/generations`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.apiKey}`, }, body: JSON.stringify({ model: config.model || DEFAULT_MODEL, prompt: options.prompt, size: resolveSeedreamSize(options), watermark: false, }), }); if (!response.ok) { const text = await response.text(); throw new Error(`Seedream generation failed (${response.status}): ${text}`); } const data = await response.json(); // OpenAI-compatible response format: { data: [{ url, b64_json, ... }] } const imageData = data.data?.[0]; if (!imageData) { throw new Error('Seedream returned empty response'); } return { url: imageData.url, base64: imageData.b64_json, width: options.width || 1024, height: options.height || 1024, }; }