mirror of
https://github.com/foxhui/WebAI2API.git
synced 2026-06-16 21:03:59 +08:00
fix: 修复豆包图片适配器 SSE 解析失败的问题
豆包图片生成的 SSE 使用 event_data 嵌套结构(三层 JSON 字符串), 而非 patch_op 扁平结构。parseSSEForImage 新增对 event_data 格式 的支持,extractRawImage 扩展支持 creations 直接结构以复用提取逻辑。
This commit is contained in:
@@ -124,6 +124,7 @@ async function generate(context, prompt, imgPaths, modelId, meta = {}) {
|
|||||||
const contentType = response.headers()['content-type'] || '';
|
const contentType = response.headers()['content-type'] || '';
|
||||||
if (!contentType.includes('text/event-stream')) return;
|
if (!contentType.includes('text/event-stream')) return;
|
||||||
|
|
||||||
|
await response.finished();
|
||||||
const body = await response.text();
|
const body = await response.text();
|
||||||
const extractedUrl = parseSSEForImage(body);
|
const extractedUrl = parseSSEForImage(body);
|
||||||
|
|
||||||
@@ -184,26 +185,44 @@ async function generate(context, prompt, imgPaths, modelId, meta = {}) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析 SSE 响应,提取图片链接
|
* 解析 SSE 响应,提取图片链接
|
||||||
|
* SSE 格式: data: {"event_data": "<json_string>", "event_type": 2001}
|
||||||
|
* event_data 解析后: {"message": {"content_type": 2074, "content": "<json_string>"}}
|
||||||
|
* content 解析后: {"creations": [{"image": {"image_ori_raw": {"url": "..."}}}]}
|
||||||
* @param {string} body - SSE 响应体
|
* @param {string} body - SSE 响应体
|
||||||
* @returns {string|null} 图片 URL
|
* @returns {string|null} 图片 URL
|
||||||
*/
|
*/
|
||||||
function parseSSEForImage(body) {
|
function parseSSEForImage(body) {
|
||||||
const lines = body.split('\n');
|
const lines = body.split('\n');
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (const line of lines) {
|
||||||
const line = lines[i].trim();
|
const trimmed = line.trim();
|
||||||
|
if (!trimmed.startsWith('data:')) continue;
|
||||||
|
|
||||||
if (line.startsWith('data:')) {
|
const dataStr = trimmed.substring(5).trim();
|
||||||
const dataLine = line.substring(5).trim();
|
if (!dataStr || dataStr === '{}') continue;
|
||||||
if (!dataLine || dataLine === '{}') continue;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(dataLine);
|
const data = JSON.parse(dataStr);
|
||||||
const url = extractRawImage(data);
|
|
||||||
if (url) return url;
|
// 新格式: event_data 嵌套结构
|
||||||
} catch (e) {
|
if (data.event_data) {
|
||||||
// JSON 解析失败,跳过
|
const eventData = typeof data.event_data === 'string'
|
||||||
|
? JSON.parse(data.event_data) : data.event_data;
|
||||||
|
const message = eventData?.message;
|
||||||
|
if (message?.content_type === 2074 && message.content) {
|
||||||
|
const content = typeof message.content === 'string'
|
||||||
|
? JSON.parse(message.content) : message.content;
|
||||||
|
const url = extractRawImage(content);
|
||||||
|
if (url) return url;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 旧格式: patch_op 扁平结构 (兼容)
|
||||||
|
const url = extractRawImage(data);
|
||||||
|
if (url) return url;
|
||||||
|
} catch (e) {
|
||||||
|
// JSON 解析失败,跳过
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,37 +231,39 @@ function parseSSEForImage(body) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 从 SSE 消息数据中提取原图 Raw 链接
|
* 从 SSE 消息数据中提取原图 Raw 链接
|
||||||
|
* 支持两种格式:
|
||||||
|
* - patch_op 格式: {patch_op: [{patch_value: {content_block: [{block_type: 2074, content: {creation_block: {creations: [...]}}}]}}]}
|
||||||
|
* - creations 格式: {creations: [{image: {image_ori_raw: {url: "..."}}}]}
|
||||||
* @param {Object} sseData - 解析后的 data JSON 对象
|
* @param {Object} sseData - 解析后的 data JSON 对象
|
||||||
* @returns {string|null} - 返回图片 URL 或 null
|
* @returns {string|null} - 返回图片 URL 或 null
|
||||||
*/
|
*/
|
||||||
function extractRawImage(sseData) {
|
function extractRawImage(sseData) {
|
||||||
if (!sseData || !sseData.patch_op || !Array.isArray(sseData.patch_op)) {
|
if (!sseData) return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const op of sseData.patch_op) {
|
// 格式 1: patch_op 结构
|
||||||
const contentBlocks = op.patch_value?.content_block;
|
if (Array.isArray(sseData.patch_op)) {
|
||||||
|
for (const op of sseData.patch_op) {
|
||||||
|
const contentBlocks = op.patch_value?.content_block;
|
||||||
|
|
||||||
if (Array.isArray(contentBlocks)) {
|
if (Array.isArray(contentBlocks)) {
|
||||||
for (const block of contentBlocks) {
|
for (const block of contentBlocks) {
|
||||||
// block_type 2074 代表生成卡片
|
if (block.block_type === 2074) {
|
||||||
if (block.block_type === 2074) {
|
const url = extractRawImage(block.content?.creation_block);
|
||||||
const creations = block.content?.creation_block?.creations;
|
if (url) return url;
|
||||||
|
|
||||||
if (Array.isArray(creations)) {
|
|
||||||
for (const creation of creations) {
|
|
||||||
// 提取 image_ori_raw,只有图片生成完成时才会出现
|
|
||||||
const rawUrl = creation.image?.image_ori_raw?.url;
|
|
||||||
if (rawUrl) {
|
|
||||||
return rawUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 格式 2: creations 直接结构
|
||||||
|
if (Array.isArray(sseData.creations)) {
|
||||||
|
for (const creation of sseData.creations) {
|
||||||
|
const rawUrl = creation.image?.image_ori_raw?.url;
|
||||||
|
if (rawUrl) return rawUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user