diff --git a/CHANGELOG.md b/CHANGELOG.md index d7dbb50..52c92da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 🐛 Fixed - **适配器** - 修复 DeepSeek 文本生成适配器因接口格式更新导致无法获取生成结果 + - 修复 豆包 适配器图片上传逻辑更改导致上传进度验证失败的问题 ## [3.5.2] - 2026-02-10 diff --git a/src/backend/adapter/doubao.js b/src/backend/adapter/doubao.js index 13bd1ad..8dd0590 100644 --- a/src/backend/adapter/doubao.js +++ b/src/backend/adapter/doubao.js @@ -60,17 +60,39 @@ async function generate(context, prompt, imgPaths, modelId, meta = {}) { if (imgPaths && imgPaths.length > 0) { logger.info('适配器', `开始上传 ${imgPaths.length} 张图片...`, meta); - const uploadBtn = page.locator('button[data-testid="image-creation-chat-input-picture-reference-button"]'); - await uploadBtn.waitFor({ state: 'visible', timeout: 10000 }); - - await uploadFilesViaChooser(page, uploadBtn, imgPaths, { - uploadValidator: (response) => { + // 预先拦截 ApplyImageUpload 响应,动态收集实际上传路径 + const expectedUploadPaths = new Set(); + const applyUploadHandler = async (response) => { + try { const url = response.url(); - return response.status() === 200 && - url.includes('bytedanceapi.com') && - url.includes('Action=CommitImageUpload'); - } - }, meta); + if (!url.includes('Action=ApplyImageUpload') || response.status() !== 200) return; + const json = await response.json(); + const storeUri = json.Result?.UploadAddress?.StoreInfos?.[0]?.StoreUri; + if (storeUri) { + expectedUploadPaths.add(storeUri); + logger.debug('适配器', `已获取上传路径: ${storeUri}`, meta); + } + } catch { /* 忽略解析错误 */ } + }; + page.on('response', applyUploadHandler); + + try { + const uploadBtn = page.locator('button[data-testid="image-creation-chat-input-picture-reference-button"]'); + await uploadBtn.waitFor({ state: 'visible', timeout: 10000 }); + + await uploadFilesViaChooser(page, uploadBtn, imgPaths, { + uploadValidator: (response) => { + if (response.status() !== 200 || response.request().method() !== 'POST') return false; + const url = response.url(); + for (const path of expectedUploadPaths) { + if (url.includes(path)) return true; + } + return false; + } + }, meta); + } finally { + page.off('response', applyUploadHandler); + } logger.info('适配器', '图片上传完成', meta); } diff --git a/src/backend/adapter/doubao_text.js b/src/backend/adapter/doubao_text.js index 43c236c..4a9eb36 100644 --- a/src/backend/adapter/doubao_text.js +++ b/src/backend/adapter/doubao_text.js @@ -45,21 +45,43 @@ async function generate(context, prompt, imgPaths, modelId, meta = {}) { if (imgPaths && imgPaths.length > 0) { logger.info('适配器', `开始上传 ${imgPaths.length} 张图片...`, meta); - // 点击上传菜单按钮 - const uploadMenuBtn = page.locator('main button[aria-haspopup="menu"]').first(); - await safeClick(page, uploadMenuBtn, { bias: 'button' }); - await sleep(300, 500); - - // 点击上传文件选项 - const uploadItem = page.locator('div[data-testid="upload_file_panel_upload_item"][role="menuitem"]'); - await uploadFilesViaChooser(page, uploadItem, imgPaths, { - uploadValidator: (response) => { + // 预先拦截 ApplyImageUpload 响应,动态收集实际上传路径 + const expectedUploadPaths = new Set(); + const applyUploadHandler = async (response) => { + try { const url = response.url(); - return response.status() === 200 && - url.includes('bytedanceapi.com') && - url.includes('Action=CommitImageUpload'); - } - }, meta); + if (!url.includes('Action=ApplyImageUpload') || response.status() !== 200) return; + const json = await response.json(); + const storeUri = json.Result?.UploadAddress?.StoreInfos?.[0]?.StoreUri; + if (storeUri) { + expectedUploadPaths.add(storeUri); + logger.debug('适配器', `已获取上传路径: ${storeUri}`, meta); + } + } catch { /* 忽略解析错误 */ } + }; + page.on('response', applyUploadHandler); + + try { + // 点击上传菜单按钮 + const uploadMenuBtn = page.locator('main button[aria-haspopup="menu"]').first(); + await safeClick(page, uploadMenuBtn, { bias: 'button' }); + await sleep(300, 500); + + // 点击上传文件选项 + const uploadItem = page.locator('div[data-testid="upload_file_panel_upload_item"][role="menuitem"]'); + await uploadFilesViaChooser(page, uploadItem, imgPaths, { + uploadValidator: (response) => { + if (response.status() !== 200 || response.request().method() !== 'POST') return false; + const url = response.url(); + for (const path of expectedUploadPaths) { + if (url.includes(path)) return true; + } + return false; + } + }, meta); + } finally { + page.off('response', applyUploadHandler); + } logger.info('适配器', '图片上传完成', meta); }