@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
119 lines (99 loc) • 3.74 kB
text/typescript
import createDebug from 'debug';
import { RuntimeImageGenParamsValue } from 'model-bank';
import OpenAI from 'openai';
import { CreateImageOptions } from '../../core/openaiCompatibleFactory';
import { CreateImagePayload, CreateImageResponse } from '../../types/image';
const log = createDebug('lobe-image:volcengine');
/**
* Volcengine image generation implementation
* Based on Volcengine API docs: https://www.volcengine.com/docs/82379/1541523
*/
export async function createVolcengineImage(
payload: CreateImagePayload,
options: CreateImageOptions,
): Promise<CreateImageResponse> {
const { model, params } = payload;
log('Creating image with Volcengine API - model: %s, params: %O', model, params);
// Create OpenAI client with Volcengine configuration
const client = new OpenAI({
apiKey: options.apiKey,
baseURL: options.baseURL || 'https://ark.cn-beijing.volces.com/api/v3',
});
// Parameter mapping: imageUrls/imageUrl -> image, cfg -> guidance_scale
const paramsMap = new Map<RuntimeImageGenParamsValue, string>([
['imageUrls', 'image'],
['imageUrl', 'image'],
['cfg', 'guidance_scale'],
]);
const userInput: Record<string, any> = Object.fromEntries(
Object.entries(params).map(([key, value]) => [
paramsMap.get(key as RuntimeImageGenParamsValue) ?? key,
value,
]),
);
// Volcengine supports direct URL or base64, no need to convert to File objects
// Check if there is image input
const hasImageInput =
userInput.image !== null &&
userInput.image !== undefined &&
(Array.isArray(userInput.image) ? userInput.image.length > 0 : true);
if (hasImageInput) {
log('Image input detected: %O', userInput.image);
} else {
delete userInput.image;
}
// Build request options
const requestOptions = {
model,
watermark: false, // Default to no watermark
...userInput,
};
log('Volcengine API options: %O', requestOptions);
// Call Volcengine image generation API
const response = await client.images.generate(requestOptions as any);
log('Volcengine API response: %O', response);
// Validate response data
if (!response || !response.data || !Array.isArray(response.data) || response.data.length === 0) {
log('Invalid response: missing data array');
throw new Error('Invalid response: missing or empty data array');
}
const imageData = response.data[0];
if (!imageData) {
log('Invalid response: first data item is null/undefined');
throw new Error('Invalid response: first data item is null or undefined');
}
let imageUrl: string;
let width: number | undefined;
let height: number | undefined;
// Handle base64 format response
if (imageData.b64_json) {
const mimeType = 'image/jpeg'; // Volcengine defaults to JPEG format
imageUrl = `data:${mimeType};base64,${imageData.b64_json}`;
log('Successfully converted base64 to data URL, length: %d', imageUrl.length);
}
// Handle URL format response
else if (imageData.url) {
imageUrl = imageData.url;
log('Using direct image URL: %s', imageUrl);
}
// If neither format exists, throw error
else {
log('Invalid response: missing both b64_json and url fields');
throw new Error('Invalid response: missing both b64_json and url fields');
}
// Extract size information (Volcengine specific)
const volcengineImageData = imageData as any;
if (volcengineImageData.size) {
const sizeMatch = volcengineImageData.size.match(/^(\d+)x(\d+)$/);
if (sizeMatch) {
width = parseInt(sizeMatch[1], 10);
height = parseInt(sizeMatch[2], 10);
log('Extracted image dimensions: %dx%d', width, height);
}
}
return {
height,
imageUrl,
width,
};
}