ZhaoShanGeng
feat: 多项修复与代码简化
873f10f
import express from 'express';
import { getAvailableModels, generateImageForSD } from '../api/client.js';
import { generateRequestBody, prepareImageRequest } from '../utils/utils.js';
import tokenManager from '../auth/token_manager.js';
import logger from '../utils/logger.js';
const router = express.Router();
// 静态数据
const SD_MOCK_DATA = {
options: {
sd_model_checkpoint: 'gemini-3-pro-image',
sd_vae: 'auto',
CLIP_stop_at_last_layers: 1
},
samplers: [
{ name: 'Euler a', aliases: ['k_euler_a'] },
{ name: 'Euler', aliases: ['k_euler'] },
{ name: 'DPM++ 2M', aliases: ['k_dpmpp_2m'] },
{ name: 'DPM++ SDE', aliases: ['k_dpmpp_sde'] }
],
schedulers: [
{ name: 'Automatic', label: 'Automatic' },
{ name: 'Uniform', label: 'Uniform' },
{ name: 'Karras', label: 'Karras' },
{ name: 'Exponential', label: 'Exponential' }
],
upscalers: [
{ name: 'None', model_name: null, scale: 1 },
{ name: 'Lanczos', model_name: null, scale: 4 },
{ name: 'ESRGAN_4x', model_name: 'ESRGAN_4x', scale: 4 }
],
latentUpscaleModes: [
{ name: 'Latent' },
{ name: 'Latent (antialiased)' },
{ name: 'Latent (bicubic)' },
{ name: 'Latent (nearest)' }
],
vae: [
{ model_name: 'auto', filename: 'auto' },
{ model_name: 'None', filename: 'None' }
],
modules: [
{ name: 'none', path: null },
{ name: 'LoRA', path: 'lora' }
],
loras: [
{ name: 'example_lora_v1', alias: 'example_lora_v1', path: 'example_lora_v1.safetensors' },
{ name: 'style_lora', alias: 'style_lora', path: 'style_lora.safetensors' }
],
embeddings: [
{ name: 'EasyNegative', step: 1, sd_checkpoint: null, sd_checkpoint_name: null },
{ name: 'badhandv4', step: 1, sd_checkpoint: null, sd_checkpoint_name: null }
],
hypernetworks: [
{ name: 'example_hypernetwork', path: 'example_hypernetwork.pt' }
],
scripts: [
{ name: 'None', is_alwayson: false, is_img2img: false },
{ name: 'X/Y/Z plot', is_alwayson: false, is_img2img: false }
],
progress: {
progress: 0,
eta_relative: 0,
state: { skipped: false, interrupted: false, job: '', job_count: 0, job_timestamp: '0', job_no: 0 },
current_image: null,
textinfo: null
}
};
// 构建图片生成请求体
function buildImageRequestBody(prompt, token) {
const messages = [{ role: 'user', content: prompt }];
const requestBody = generateRequestBody(messages, 'gemini-3-pro-image', {}, null, token);
return prepareImageRequest(requestBody);
}
// GET 路由
router.get('/sd-models', async (req, res) => {
try {
const models = await getAvailableModels();
const imageModels = models.data
.filter(m => m.id.includes('-image'))
.map(m => ({
title: m.id,
model_name: m.id,
hash: null,
sha256: null,
filename: m.id,
config: null
}));
res.json(imageModels);
} catch (error) {
logger.error('获取SD模型列表失败:', error.message);
res.status(500).json({ error: error.message });
}
});
router.get('/options', (req, res) => res.json(SD_MOCK_DATA.options));
router.get('/samplers', (req, res) => res.json(SD_MOCK_DATA.samplers));
router.get('/schedulers', (req, res) => res.json(SD_MOCK_DATA.schedulers));
router.get('/upscalers', (req, res) => res.json(SD_MOCK_DATA.upscalers));
router.get('/latent-upscale-modes', (req, res) => res.json(SD_MOCK_DATA.latentUpscaleModes));
router.get('/sd-vae', (req, res) => res.json(SD_MOCK_DATA.vae));
router.get('/sd-modules', (req, res) => res.json(SD_MOCK_DATA.modules));
router.get('/loras', (req, res) => res.json(SD_MOCK_DATA.loras));
router.get('/embeddings', (req, res) => res.json({ loaded: SD_MOCK_DATA.embeddings, skipped: {} }));
router.get('/hypernetworks', (req, res) => res.json(SD_MOCK_DATA.hypernetworks));
router.get('/scripts', (req, res) => res.json({ txt2img: SD_MOCK_DATA.scripts, img2img: SD_MOCK_DATA.scripts }));
router.get('/script-info', (req, res) => res.json([]));
router.get('/progress', (req, res) => res.json(SD_MOCK_DATA.progress));
router.get('/cmd-flags', (req, res) => res.json({}));
router.get('/memory', (req, res) => res.json({ ram: { free: 8589934592, used: 8589934592, total: 17179869184 }, cuda: { system: { free: 0, used: 0, total: 0 } } }));
// POST 路由
router.post('/img2img', async (req, res) => {
const { prompt, init_images } = req.body;
try {
if (!prompt) {
return res.status(400).json({ error: 'prompt is required' });
}
const token = await tokenManager.getToken();
if (!token) {
throw new Error('没有可用的token');
}
// 构建包含图片的消息
const content = [{ type: 'text', text: prompt }];
if (init_images && init_images.length > 0) {
init_images.forEach(img => {
const format = img.startsWith('/9j/') ? 'jpeg' : 'png';
content.push({ type: 'image_url', image_url: { url: `data:image/${format};base64,${img}` } });
});
}
const messages = [{ role: 'user', content }];
const requestBody = prepareImageRequest(
generateRequestBody(messages, 'gemini-3-pro-image', {}, null, token)
);
const images = await generateImageForSD(requestBody, token);
if (images.length === 0) {
throw new Error('未生成图片');
}
res.json({
images,
parameters: req.body,
info: JSON.stringify({ prompt })
});
} catch (error) {
logger.error('SD图生图失败:', error.message);
res.status(500).json({ error: error.message });
}
});
router.post('/txt2img', async (req, res) => {
const { prompt, negative_prompt, steps, cfg_scale, width, height, seed, sampler_name } = req.body;
try {
if (!prompt) {
return res.status(400).json({ error: 'prompt is required' });
}
const token = await tokenManager.getToken();
if (!token) {
throw new Error('没有可用的token');
}
const requestBody = buildImageRequestBody(prompt, token);
const images = await generateImageForSD(requestBody, token);
if (images.length === 0) {
throw new Error('未生成图片');
}
res.json({
images,
parameters: { prompt, negative_prompt, steps, cfg_scale, width, height, seed, sampler_name },
info: JSON.stringify({ prompt, seed: seed || -1 })
});
} catch (error) {
logger.error('SD生图失败:', error.message);
res.status(500).json({ error: error.message });
}
});
router.post('/options', (req, res) => res.json({}));
router.post('/refresh-checkpoints', (req, res) => res.json(null));
router.post('/refresh-loras', (req, res) => res.json(null));
router.post('/interrupt', (req, res) => res.json(null));
router.post('/skip', (req, res) => res.json(null));
export default router;