deepseek reasoner Closes #3322
| @@ -1977,12 +1977,12 @@ | |||
| 1977 | </span> | 1977 | </span> |
| 1978 | </div> | 1978 | </div> |
| 1979 | </div> | 1979 | </div> |
| 1980 | <div class="range-block" data-source="makersuite"> | 1980 | <div class="range-block" data-source="makersuite,deepseek"> |
| 1981 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> | 1981 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> |
| 1982 | <input id="openai_show_thoughts" type="checkbox" /> | 1982 | <input id="openai_show_thoughts" type="checkbox" /> |
| 1983 | <span> | 1983 | <span> |
| 1984 | <span data-i18n="Show model thoughts">Show model thoughts</span> | 1984 | <span data-i18n="Show model thoughts">Show model thoughts</span> |
| 1985 | <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking"></i> | 1985 | <i class="opacity50p fa-solid fa-circle-info" title="Gemini 2.0 Thinking / DeepSeek Reasoner"></i> |
| 1986 | </span> | 1986 | </span> |
| 1987 | </label> | 1987 | </label> |
| 1988 | <div class="toggle-description justifyLeft marginBot5"> | 1988 | <div class="toggle-description justifyLeft marginBot5"> |
| @@ -3209,6 +3209,7 @@ | |||
| 3209 | <select id="model_deepseek_select"> | 3209 | <select id="model_deepseek_select"> |
| 3210 | <option value="deepseek-chat">deepseek-chat</option> | 3210 | <option value="deepseek-chat">deepseek-chat</option> |
| 3211 | <option value="deepseek-coder">deepseek-coder</option> | 3211 | <option value="deepseek-coder">deepseek-coder</option> |
| 3212 | <option value="deepseek-reasoner">deepseek-reasoner</option> | ||
| 3212 | </select> | 3213 | </select> |
| 3213 | </div> | 3214 | </div> |
| 3214 | </div> | 3215 | </div> |
| @@ -5655,6 +5655,7 @@ function extractMessageFromData(data) { | |||
| 5655 | return data; | 5655 | return data; |
| 5656 | } | 5656 | } |
| 5657 | 5657 | ||
| 5658 | function getTextContext() { | ||
| 5658 | switch (main_api) { | 5659 | switch (main_api) { |
| 5659 | case 'kobold': | 5660 | case 'kobold': |
| 5660 | return data.results[0].text; | 5661 | return data.results[0].text; |
| @@ -5671,6 +5672,16 @@ function extractMessageFromData(data) { | |||
| 5671 | } | 5672 | } |
| 5672 | } | 5673 | } |
| 5673 | 5674 | ||
| 5675 | const content = getTextContext(); | ||
| 5676 | |||
| 5677 | if (main_api === 'openai' && oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK && oai_settings.show_thoughts) { | ||
| 5678 | const thoughts = data?.choices?.[0]?.message?.reasoning_content ?? ''; | ||
| 5679 | return [thoughts, content].filter(x => x).join('\n\n'); | ||
| 5680 | } | ||
| 5681 | |||
| 5682 | return content; | ||
| 5683 | } | ||
| 5684 | |||
| 5674 | /** | 5685 | /** |
| 5675 | * Extracts multiswipe swipes from the response data. | 5686 | * Extracts multiswipe swipes from the response data. |
| 5676 | * @param {Object} data Response data | 5687 | * @param {Object} data Response data |
| @@ -2030,6 +2030,16 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2030 | // https://api-docs.deepseek.com/api/create-chat-completion | 2030 | // https://api-docs.deepseek.com/api/create-chat-completion |
| 2031 | if (isDeepSeek) { | 2031 | if (isDeepSeek) { |
| 2032 | generate_data.top_p = generate_data.top_p || Number.EPSILON; | 2032 | generate_data.top_p = generate_data.top_p || Number.EPSILON; |
| 2033 | |||
| 2034 | if (generate_data.model.endsWith('-reasoner')) { | ||
| 2035 | delete generate_data.top_p; | ||
| 2036 | delete generate_data.temperature; | ||
| 2037 | delete generate_data.frequency_penalty; | ||
| 2038 | delete generate_data.presence_penalty; | ||
| 2039 | delete generate_data.top_logprobs; | ||
| 2040 | delete generate_data.logprobs; | ||
| 2041 | delete generate_data.logit_bias; | ||
| 2042 | } | ||
| 2033 | } | 2043 | } |
| 2034 | 2044 | ||
| 2035 | if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) { | 2045 | if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) { |
| @@ -2085,6 +2095,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2085 | let text = ''; | 2095 | let text = ''; |
| 2086 | const swipes = []; | 2096 | const swipes = []; |
| 2087 | const toolCalls = []; | 2097 | const toolCalls = []; |
| 2098 | const state = {}; | ||
| 2088 | while (true) { | 2099 | while (true) { |
| 2089 | const { done, value } = await reader.read(); | 2100 | const { done, value } = await reader.read(); |
| 2090 | if (done) return; | 2101 | if (done) return; |
| @@ -2095,9 +2106,9 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2095 | 2106 | ||
| 2096 | if (Array.isArray(parsed?.choices) && parsed?.choices?.[0]?.index > 0) { | 2107 | if (Array.isArray(parsed?.choices) && parsed?.choices?.[0]?.index > 0) { |
| 2097 | const swipeIndex = parsed.choices[0].index - 1; | 2108 | const swipeIndex = parsed.choices[0].index - 1; |
| 2098 | swipes[swipeIndex] = (swipes[swipeIndex] || '') + getStreamingReply(parsed); | 2109 | swipes[swipeIndex] = (swipes[swipeIndex] || '') + getStreamingReply(parsed, state); |
| 2099 | } else { | 2110 | } else { |
| 2100 | text += getStreamingReply(parsed); | 2111 | text += getStreamingReply(parsed, state); |
| 2101 | } | 2112 | } |
| 2102 | 2113 | ||
| 2103 | ToolManager.parseToolCalls(toolCalls, parsed); | 2114 | ToolManager.parseToolCalls(toolCalls, parsed); |
| @@ -2129,13 +2140,26 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2129 | } | 2140 | } |
| 2130 | } | 2141 | } |
| 2131 | 2142 | ||
| 2132 | function getStreamingReply(data) { | 2143 | /** |
| 2144 | * Extracts the reply from the response data from a chat completions-like source | ||
| 2145 | * @param {object} data Response data from the chat completions-like source | ||
| 2146 | * @param {object} state Additional state to keep track of | ||
| 2147 | * @returns {string} The reply extracted from the response data | ||
| 2148 | */ | ||
| 2149 | function getStreamingReply(data, state) { | ||
| 2133 | if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) { | 2150 | if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) { |
| 2134 | return data?.delta?.text || ''; | 2151 | return data?.delta?.text || ''; |
| 2135 | } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) { | 2152 | } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) { |
| 2136 | return data?.candidates?.[0]?.content?.parts?.filter(x => oai_settings.show_thoughts || !x.thought)?.map(x => x.text)?.filter(x => x)?.join('\n\n') || ''; | 2153 | return data?.candidates?.[0]?.content?.parts?.filter(x => oai_settings.show_thoughts || !x.thought)?.map(x => x.text)?.filter(x => x)?.join('\n\n') || ''; |
| 2137 | } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { | 2154 | } else if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { |
| 2138 | return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || ''; | 2155 | return data?.delta?.message?.content?.text || data?.delta?.message?.tool_plan || ''; |
| 2156 | } else if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) { | ||
| 2157 | const hadThoughts = state.hadThoughts; | ||
| 2158 | const thoughts = data.choices?.filter(x => oai_settings.show_thoughts || !x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''; | ||
| 2159 | const content = data.choices?.[0]?.delta?.content || ''; | ||
| 2160 | state.hadThoughts = !!thoughts; | ||
| 2161 | const separator = hadThoughts && !thoughts ? '\n\n' : ''; | ||
| 2162 | return [thoughts, separator, content].filter(x => x).join('\n\n'); | ||
| 2139 | } else { | 2163 | } else { |
| 2140 | return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; | 2164 | return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? ''; |
| 2141 | } | 2165 | } |
| @@ -4488,7 +4512,7 @@ async function onModelChange() { | |||
| 4488 | if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) { | 4512 | if (oai_settings.chat_completion_source === chat_completion_sources.DEEPSEEK) { |
| 4489 | if (oai_settings.max_context_unlocked) { | 4513 | if (oai_settings.max_context_unlocked) { |
| 4490 | $('#openai_max_context').attr('max', unlocked_max); | 4514 | $('#openai_max_context').attr('max', unlocked_max); |
| 4491 | } else if (oai_settings.deepseek_model == 'deepseek-chat') { | 4515 | } else if (['deepseek-reasoner', 'deepseek-chat'].includes(oai_settings.deepseek_model)) { |
| 4492 | $('#openai_max_context').attr('max', max_64k); | 4516 | $('#openai_max_context').attr('max', max_64k); |
| 4493 | } else if (oai_settings.deepseek_model == 'deepseek-coder') { | 4517 | } else if (oai_settings.deepseek_model == 'deepseek-coder') { |
| 4494 | $('#openai_max_context').attr('max', max_16k); | 4518 | $('#openai_max_context').attr('max', max_16k); |
| @@ -220,6 +220,21 @@ async function* parseStreamData(json) { | |||
| 220 | } | 220 | } |
| 221 | return; | 221 | return; |
| 222 | } | 222 | } |
| 223 | else if (typeof json.choices[0].delta.reasoning_content === 'string' && json.choices[0].delta.reasoning_content.length > 0) { | ||
| 224 | for (let j = 0; j < json.choices[0].delta.reasoning_content.length; j++) { | ||
| 225 | const str = json.choices[0].delta.reasoning_content[j]; | ||
| 226 | const isLastSymbol = j === json.choices[0].delta.reasoning_content.length - 1; | ||
| 227 | const choiceClone = structuredClone(json.choices[0]); | ||
| 228 | choiceClone.delta.reasoning_content = str; | ||
| 229 | choiceClone.delta.content = isLastSymbol ? choiceClone.delta.content : ''; | ||
| 230 | const choices = [choiceClone]; | ||
| 231 | yield { | ||
| 232 | data: { ...json, choices }, | ||
| 233 | chunk: str, | ||
| 234 | }; | ||
| 235 | } | ||
| 236 | return; | ||
| 237 | } | ||
| 223 | else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) { | 238 | else if (typeof json.choices[0].delta.content === 'string' && json.choices[0].delta.content.length > 0) { |
| 224 | for (let j = 0; j < json.choices[0].delta.content.length; j++) { | 239 | for (let j = 0; j < json.choices[0].delta.content.length; j++) { |
| 225 | const str = json.choices[0].delta.content[j]; | 240 | const str = json.choices[0].delta.content[j]; |
| @@ -61,6 +61,7 @@ const API_DEEPSEEK = 'https://api.deepseek.com/beta'; | |||
| 61 | * @returns | 61 | * @returns |
| 62 | */ | 62 | */ |
| 63 | function postProcessPrompt(messages, type, names) { | 63 | function postProcessPrompt(messages, type, names) { |
| 64 | const addAssistantPrefix = x => x.length && (x[x.length - 1].role !== 'assistant' || (x[x.length - 1].prefix = true)) ? x : x; | ||
| 64 | switch (type) { | 65 | switch (type) { |
| 65 | case 'merge': | 66 | case 'merge': |
| 66 | case 'claude': | 67 | case 'claude': |
| @@ -70,7 +71,9 @@ function postProcessPrompt(messages, type, names) { | |||
| 70 | case 'strict': | 71 | case 'strict': |
| 71 | return mergeMessages(messages, names, true, true); | 72 | return mergeMessages(messages, names, true, true); |
| 72 | case 'deepseek': | 73 | case 'deepseek': |
| 73 | return (x => x.length && (x[x.length - 1].role !== 'assistant' || (x[x.length - 1].prefix = true)) ? x : x)(mergeMessages(messages, names, true, false)); | 74 | return addAssistantPrefix(mergeMessages(messages, names, true, false)); |
| 75 | case 'deepseek-reasoner': | ||
| 76 | return addAssistantPrefix(mergeMessages(messages, names, true, true)); | ||
| 74 | default: | 77 | default: |
| 75 | return messages; | 78 | return messages; |
| 76 | } | 79 | } |
| @@ -965,7 +968,8 @@ router.post('/generate', jsonParser, function (request, response) { | |||
| 965 | bodyParams['logprobs'] = true; | 968 | bodyParams['logprobs'] = true; |
| 966 | } | 969 | } |
| 967 | 970 | ||
| 968 | request.body.messages = postProcessPrompt(request.body.messages, 'deepseek', getPromptNames(request)); | 971 | const postProcessType = String(request.body.model).endsWith('-reasoner') ? 'deepseek-reasoner' : 'deepseek'; |
| 972 | request.body.messages = postProcessPrompt(request.body.messages, postProcessType, getPromptNames(request)); | ||
| 969 | } else { | 973 | } else { |
| 970 | console.log('This chat completion source is not supported yet.'); | 974 | console.log('This chat completion source is not supported yet.'); |
| 971 | return response.status(400).send({ error: true }); | 975 | return response.status(400).send({ error: true }); |