diff --git a/components/Main.vue b/components/Main.vue index a4f1bce..d8615f3 100644 --- a/components/Main.vue +++ b/components/Main.vue @@ -398,6 +398,25 @@ + + + + + API 格式 + + + + + + + + + + + @@ -760,6 +779,8 @@ let compute = ref({ showNewAPI: computed(() => servicesType.isNewApi(config.value.service)), // 14、是否显示Azure OpenAI端点配置 showAzureOpenaiEndpoint: computed(() => servicesType.isAzureOpenai(config.value.service)), + // 15、是否显示 DeepSeek API 格式配置 + showDeepseekApiType: computed(() => config.value.service === 'deepseek'), }) // 监听主题变化 diff --git a/docs/config/translation-engines.md b/docs/config/translation-engines.md index c3f238b..dadd62f 100644 --- a/docs/config/translation-engines.md +++ b/docs/config/translation-engines.md @@ -48,7 +48,7 @@ 2. 点击设置图标,进入设置页面 3. 选择 "翻译引擎" 选项卡 4. 在引擎列表中选择 "DeepSeek" -5. 选择 `deepseek-chat` 模型,并填入配置信息 +5. 选择 `deepseek-v4-flash` 模型,并填入配置信息 点击流畅阅读图标 diff --git a/entrypoints/service/deepseek.ts b/entrypoints/service/deepseek.ts index c7c30f2..39e3965 100644 --- a/entrypoints/service/deepseek.ts +++ b/entrypoints/service/deepseek.ts @@ -1,8 +1,19 @@ import { method, urls } from "../utils/constant"; -import { deepseekMsgTemplate } from "../utils/template"; +import { deepseekMsgTemplate, deepseekResponsesMsgTemplate, getCurrentModel } from "../utils/template"; import { config } from "@/entrypoints/utils/config"; import { contentPostHandler } from "@/entrypoints/utils/check"; +// deepseek-v4-* 系列模型走官方的 Responses API(POST /responses) +// 其他模型(含自定义模型)走 OpenAI 兼容的 chat completions 接口 +// API 格式由设置中的 deepseekApiType 控制:'responses' 强制 Responses API,'chat' 强制 Chat Completion,'auto' 按模型支持情况自动选择 +function useResponsesApi(model: string) { + const apiType = config.deepseekApiType; + if (apiType === 'responses') return true; + if (apiType === 'chat') return false; + // auto: v4-flash 官方原生支持 Responses API;v4-pro 支持尚在灰度,走 chat completions 最稳 + return model === 'deepseek-v4-flash'; +} + async function deepseek(message: any) { try { const headers = new Headers({ @@ -10,12 +21,23 @@ async function deepseek(message: any) { 'Authorization': `Bearer ${config.token[config.service]}` }); - const url = config.proxy[config.service] || urls[config.service]; + const model = getCurrentModel(); + const endpoint = config.proxy[config.service] || urls[config.service]; + + // 去掉可能存在的 /chat/completions 后缀得到 base URL,再按所选 API 格式拼接端点。 + // 这样无论配置的是完整端点(.../chat/completions)还是 base URL(.../v1), + // payload 与端点都由同一个 isResponses 决定,不会出现格式错配。 + const isResponses = useResponsesApi(model); + // 去掉 /chat/completions 后缀和多余的尾斜杠,得到干净的 base URL + const baseUrl = endpoint.replace(/\/chat\/completions\/?$/, '').replace(/\/+$/, ''); + const url = isResponses ? `${baseUrl}/responses` : `${baseUrl}/chat/completions`; const resp = await fetch(url, { method: method.POST, headers, - body: deepseekMsgTemplate(message.origin) + body: isResponses + ? deepseekResponsesMsgTemplate(message.origin) + : deepseekMsgTemplate(message.origin) }); if (!resp.ok) { @@ -23,11 +45,33 @@ async function deepseek(message: any) { } const result = await resp.json(); - return contentPostHandler(result.choices[0].message.content); + + // Responses API: output 数组中的 message 类型内容;chat completions: choices[0].message.content + if (isResponses) { + if (typeof result.output_text === 'string' && result.output_text) { + return contentPostHandler(result.output_text); + } + const output = result.output || []; + const text = output + .filter((item: any) => item.type === 'message' && item.content) + .flatMap((item: any) => item.content) + .filter((part: any) => part.type === 'output_text') + .map((part: any) => part.text) + .join(''); + if (text) { + return contentPostHandler(text); + } + throw new Error('翻译失败: 上游未返回内容'); + } + + if (result.choices && result.choices.length > 0) { + return contentPostHandler(result.choices[0].message.content); + } + throw new Error('翻译失败: 上游未返回内容'); } catch (error) { console.error('API调用失败:', error); throw error; } } -export default deepseek; \ No newline at end of file +export default deepseek; diff --git a/entrypoints/service/newapi.ts b/entrypoints/service/newapi.ts index 87ac161..05cbeb8 100644 --- a/entrypoints/service/newapi.ts +++ b/entrypoints/service/newapi.ts @@ -1,5 +1,5 @@ import { method, urls } from "../utils/constant"; -import {commonMsgTemplate, deepseekMsgTemplate} from "../utils/template"; +import {commonMsgTemplate} from "../utils/template"; import { config } from "@/entrypoints/utils/config"; import { contentPostHandler } from "@/entrypoints/utils/check"; diff --git a/entrypoints/utils/model.ts b/entrypoints/utils/model.ts index 915e58c..c65fba2 100644 --- a/entrypoints/utils/model.ts +++ b/entrypoints/utils/model.ts @@ -53,6 +53,7 @@ export class Config { translationStatus: boolean; // 是否启用全文翻译进度面板 inputBoxTranslationTrigger: string; // 输入框翻译触发方式 inputBoxTranslationTarget: string; // 输入框翻译目标语言 + deepseekApiType: string; // DeepSeek API 格式: 'auto' | 'responses' | 'chat' constructor() { this.on = true; @@ -98,6 +99,7 @@ export class Config { this.translationStatus = true; // 默认启用翻译进度面板 this.inputBoxTranslationTrigger = 'disabled'; // 默认关闭输入框翻译 this.inputBoxTranslationTarget = 'en'; // 默认翻译成英文 + this.deepseekApiType = 'auto'; // DeepSeek 默认自动选择 API 格式 } } diff --git a/entrypoints/utils/option.ts b/entrypoints/utils/option.ts index 8293314..b0139dc 100644 --- a/entrypoints/utils/option.ts +++ b/entrypoints/utils/option.ts @@ -186,7 +186,7 @@ export const models = new Map>([ [services.infini, ["llama-2-13b-chat", "llama-3.3-70b-instruct", "qwen2.5-14b-instruct", "gemma-2-27b-it", "glm-4-9b-chat", customModelString]], [services.baichuan, ["Baichuan4-Air", "Baichuan4-Turbo", "Baichuan4", customModelString]], [services.lingyi, ["yi-lightning", customModelString]], - [services.deepseek, ["deepseek-chat", "deepseek-reasoner", customModelString]], + [services.deepseek, ["deepseek-v4-flash", "deepseek-v4-pro", customModelString]], [services.minimax, ["chatcompletion_v2"]], [services.jieyue, ["step-1-8k", customModelString]], [services.huanYuan, ["hunyuan-turbos-latest", "hunyuan-t1-latest", "hunyuan-a13b", "hunyuan-lite", "hunyuan-standard", customModelString]], @@ -220,6 +220,12 @@ export const options = { {value: false, label: "关闭"}, ], form: [{value: "auto", label: "自动检测"}], + // DeepSeek API 格式(仅 DeepSeek 服务显示) + deepseekApiType: [ + {value: "auto", label: "自动(推荐)"}, + {value: "responses", label: "Responses API"}, + {value: "chat", label: "Chat Completion"}, + ], to: [ {value: "zh-Hans", label: "中文"}, {value: "en", label: "英语"}, diff --git a/entrypoints/utils/template.ts b/entrypoints/utils/template.ts index 865b492..2f13889 100644 --- a/entrypoints/utils/template.ts +++ b/entrypoints/utils/template.ts @@ -25,12 +25,45 @@ export function commonMsgTemplate(origin: string) { } // deepseek -export function deepseekMsgTemplate(origin: string) { +export function getCurrentModel() { // 检测是否使用自定义模型 let model = config.model[config.service] === customModelString ? config.customModel[config.service] : config.model[config.service] // 删除模型名称中的中文括号及其内容,如"gpt-4(推荐)" -> "gpt-4" - model = model.replace(/(.*)/g, ""); + return model.replace(/(.*)/g, ""); +} + +// DeepSeek 温度参数:thinking 模式(deepseek-reasoner)下 temperature 无效,不传;其余模型统一 0.7 +// 两种 API 格式共用同一逻辑,保证行为一致 +function deepseekTemperature(model: string): number | undefined { + return model === 'deepseek-reasoner' ? undefined : 0.7; +} + +// DeepSeek Responses API 格式(POST /responses),仅 deepseek-v4-* 系列模型支持 +// 参考: https://api-docs.deepseek.com/guides/responses_api/ +export function deepseekResponsesMsgTemplate(origin: string) { + const model = getCurrentModel(); + let system = config.system_role[config.service] || defaultOption.system_role; + let user = (config.user_role[config.service] || defaultOption.user_role) + .replace('{{to}}', config.to).replace('{{origin}}', origin); + + const payload: any = { + 'model': model, + 'instructions': system, + 'input': user, + }; + + const temperature = deepseekTemperature(model); + if (temperature !== undefined) { + payload.temperature = temperature; + } + + return JSON.stringify(payload); +} + +// DeepSeek chat completions 格式(POST /chat/completions),兼容自定义模型与第三方 OpenAI 兼容端点 +export function deepseekMsgTemplate(origin: string) { + const model = getCurrentModel(); let system = config.system_role[config.service] || defaultOption.system_role; let user = (config.user_role[config.service] || defaultOption.user_role) @@ -44,9 +77,9 @@ export function deepseekMsgTemplate(origin: string) { ] }; - // 如果不是 deepseek-reasoner 模型,则添加 temperature - if (model !== 'deepseek-reasoner') { - payload.temperature = 0.7; + const temperature = deepseekTemperature(model); + if (temperature !== undefined) { + payload.temperature = temperature; } return JSON.stringify(payload);