deotalandAi/packages/utils/src/servers/giminiserver.js

227 lines
8.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { request as requestUtils } from '../utils/request.js'
import * as clientApi from '../api/frontend/index.js'
import * as adminApi from '../api/FrontendDesigner'
import { GoogleGenAI, Type, Modality } from "@google/genai";
const API_KEY = 'AIzaSyBmPgJKMnG7afAXR9JW14I5XSkOd_NwCVM';
const ai = API_KEY ? new GoogleGenAI({ apiKey: API_KEY }) : null;
import { FileServer } from './fileserver';
// 获取环境变量中的
const getPorjectType = () => {
// 浏览器环境
if (typeof window !== 'undefined') {
// Vite 环境变量
return import.meta.env.VITE_PROJECTTYPE;
}
// Node.js 环境
if (typeof process !== 'undefined') {
return process.env.VITE_PROJECTTYPE ;
}
};
export class GiminiServer extends FileServer {
RULE = getPorjectType();
// 任务并发队列
static taskQueue = new Map();
//最高并发限制
static MAX_CONCURRENT_TASKS = 4;
constructor() {
super();
}
/**
* 从URL获取MIME类型
* @param {*} url 图片URL
* @returns MIME类型
*/
getMimeTypeFromUrl(url) {
// 检查url是否为字符串
if (typeof url !== 'string') {
return 'image/jpeg'; // 默认为jpeg
}
try {
const extension = url.split('.').pop().toLowerCase().split('?')[0];
const mimeTypes = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'bmp': 'image/bmp',
'webp': 'image/webp',
'svg': 'image/svg+xml'
};
return mimeTypes[extension] || 'image/jpeg'; // 默认为jpeg
} catch (error) {
return 'image/jpeg'; // 出错时返回默认类型
}
}
/**
* 将图片转换为GenerativePart
* @param {*} dataUrl 图片base64编码或者url
* @param {*} type 图片类型base64或者url
* @returns GenerativePart对象
*/
dataUrlToGenerativePart =async (dataUrl,type='base64') => {
if (!dataUrl) return null;
// 确保dataUrl是字符串
if (typeof dataUrl !== 'string') {
throw new Error("dataUrl must be a string");
}
// 处理URL类型
if(type === 'url'){
return {
img_url: dataUrl,
img_type: await this.getMimeTypeFromUrl(dataUrl),
};
}
// 处理base64类型
if(type === 'base64'){
dataUrl = await this.fileToBase64(dataUrl);
}
const parts = dataUrl.split(',');
const mimeType = parts[0].match(/:(.*?);/)?.[1];
const base64Data = parts[1];
if (!mimeType || !base64Data) {
throw new Error("Invalid data URL format");
}
return {
inlineData: {
data: base64Data,
mimeType: mimeType,
},
};
};
//本地生图模型
generateImageFromMultipleImages = async (baseImages, prompt, options = {}) => {
return new Promise(async (resolve, reject) => {
const { maxImages = 5 } = options;
// 标准化输入:确保 baseImages 是数组
const images = Array.isArray(baseImages) ? baseImages : [baseImages];
try {
if (images.length > maxImages) {
reject(`参考图片数量不能超过 ${maxImages}`);
}
if (!prompt || !prompt.trim()) {
reject('请提供图片生成提示词');
}
// 处理多个参考图片
const imageParts = await Promise.all(images.map(async image =>{
return await this.dataUrlToGenerativePart(image);
} ));
// 构建请求的 parts 数组
const parts = [
...imageParts,
{ text: prompt }
];
// 执行AI请求
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash-image',
contents: {
parts: parts,
},
config: {
responseModalities: [Modality.IMAGE],
},
});
console.log(response,'图片结果');
let resultImg ;//返回的图片
// 处理响应,提取图片数据
for (const part of response.candidates[0].content.parts) {
if (part.inlineData) {
const base64ImageBytes = part.inlineData.data;
const mimeType = part.inlineData.mimeType;
resultImg = `data:${mimeType};base64,${base64ImageBytes}`;
resolve(resultImg);
break;
}
}
} catch (error) {
reject(error);
console.log(error, 'errorerrorerrorerrorerrorerror');
}
})
};
//线上生图片模型
async generateImageFromMultipleImagesOnline(baseImages, prompt,config={}){
if(GiminiServer.taskQueue.size >= GiminiServer.MAX_CONCURRENT_TASKS){
window.setElMessage({
type:'warning',
message:'Concurrent limit reached'
})
return Promise.reject('Concurrent limit reached');
}
const taskId = new Date().getTime();
GiminiServer.taskQueue.set(taskId, taskId);
return new Promise(async (resolve, reject) => {
// 标准化输入:确保 baseImages 是数组
baseImages = Array.isArray(baseImages) ? baseImages : [baseImages];
const images = await Promise.all(baseImages.map(async (image) => {
// if(image.indexOf('tcww')!=-1){
// return this.concatUrl('/upload/3e1e9ac2f08b486faca094671d793e25');
// }
return await this.uploadFile(image);
}));
try {
if (images.length > 5) {
reject(`参考图片数量不能超过5张`);
}
if (!prompt || !prompt.trim()) {
reject('请提供图片生成提示词');
}
// 处理多个参考图片
const imageParts = await Promise.all(images.map(async image =>{
return await this.dataUrlToGenerativePart(image,'url');
} ));
const params = {
"aspect_ratio": "9:16",
"model": "gemini-2.5-flash-image",//models/gemini-3-pro-image-preview/"gemini-2.5-flash-image",
"location": "global",
"vertexai": true,
...config,
inputs: [
...imageParts,
{ text: prompt }
]
}
const requestUrl = this.RULE=='admin'?adminApi.default.GENERATE_IMAGE_ADMIN:clientApi.default.GENERATE_IMAGE;
const response = await requestUtils.common(requestUrl, params);
if(response.data.error){
reject(response.data.error.message);
return;
}
if(response.code!=0){
reject(response.msg);
return;
}
let data = response.data;
// let resultImg = this.concatUrl(data?.urls[0]?.url || '');
let resultImg = data?.urls[0]?.url
// 处理响应,提取图片数据
resolve(resultImg);
} catch (error) {
reject(error);
console.log(error, 'errorerrorerrorerrorerrorerror');
} finally {
// 任务完成后从队列中移除
GiminiServer.taskQueue.delete(taskId);
}
})
}
//模型生图功能
async handleGenerateImage(referenceImages = [], prompt = '',config) {
return new Promise(async (resolve,reject) => {
// let result = await this.generateImageFromMultipleImages(referenceImages, prompt);
try {
let result = await this.generateImageFromMultipleImagesOnline(referenceImages, prompt,config);
resolve(result);
} catch (error) {
reject(error);
}
})
}
}