Merge pull request #3971 from SillyTavern/ccllaauuddee Assorted Claude adjustments
Signed| @@ -1963,7 +1963,7 @@ | |||
| 1963 | </span> | 1963 | </span> |
| 1964 | </div> | 1964 | </div> |
| 1965 | </div> | 1965 | </div> |
| 1966 | <div class="range-block" data-source="makersuite,openrouter"> | 1966 | <div class="range-block" data-source="makersuite,openrouter,claude"> |
| 1967 | <label for="openai_enable_web_search" class="checkbox_label flexWrap widthFreeExpand"> | 1967 | <label for="openai_enable_web_search" class="checkbox_label flexWrap widthFreeExpand"> |
| 1968 | <input id="openai_enable_web_search" type="checkbox" /> | 1968 | <input id="openai_enable_web_search" type="checkbox" /> |
| 1969 | <span data-i18n="Enable web search">Enable web search</span> | 1969 | <span data-i18n="Enable web search">Enable web search</span> |
| @@ -148,7 +148,8 @@ async function sendClaudeRequest(request, response) { | |||
| 148 | const useSystemPrompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt; | 148 | const useSystemPrompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt; |
| 149 | const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, getPromptNames(request)); | 149 | const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, getPromptNames(request)); |
| 150 | const useThinking = request.body.model.startsWith('claude-3-7') && Boolean(request.body.include_reasoning); | 150 | const useThinking = request.body.model.startsWith('claude-3-7') && Boolean(request.body.include_reasoning); |
| 151 | let voidPrefill = false; | 151 | const useWebSearch = /^claude-3-(5|7)/.test(request.body.model) && Boolean(request.body.enable_web_search); |
| 152 | let fixThinkingPrefill = false; | ||
| 152 | // Add custom stop sequences | 153 | // Add custom stop sequences |
| 153 | const stopSequences = []; | 154 | const stopSequences = []; |
| 154 | if (Array.isArray(request.body.stop)) { | 155 | if (Array.isArray(request.body.stop)) { |
| @@ -183,15 +184,19 @@ async function sendClaudeRequest(request, response) { | |||
| 183 | .map(tool => tool.function) | 184 | .map(tool => tool.function) |
| 184 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); | 185 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); |
| 185 | 186 | ||
| 186 | if (requestBody.tools.length) { | ||
| 187 | // No prefill when using tools | ||
| 188 | voidPrefill = true; | ||
| 189 | } | ||
| 190 | if (enableSystemPromptCache && requestBody.tools.length) { | 187 | if (enableSystemPromptCache && requestBody.tools.length) { |
| 191 | requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral' }; | 188 | requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral' }; |
| 192 | } | 189 | } |
| 193 | } | 190 | } |
| 194 | 191 | ||
| 192 | if (useWebSearch) { | ||
| 193 | const webSearchTool = [{ | ||
| 194 | 'type': 'web_search_20250305', | ||
| 195 | 'name': 'web_search', | ||
| 196 | }]; | ||
| 197 | requestBody.tools = [...(requestBody.tools || []), ...webSearchTool]; | ||
| 198 | } | ||
| 199 | |||
| 195 | if (cachingAtDepth !== -1) { | 200 | if (cachingAtDepth !== -1) { |
| 196 | cachingAtDepthForClaude(convertedPrompt.messages, cachingAtDepth); | 201 | cachingAtDepthForClaude(convertedPrompt.messages, cachingAtDepth); |
| 197 | } | 202 | } |
| @@ -200,11 +205,12 @@ async function sendClaudeRequest(request, response) { | |||
| 200 | betaHeaders.push('prompt-caching-2024-07-31'); | 205 | betaHeaders.push('prompt-caching-2024-07-31'); |
| 201 | } | 206 | } |
| 202 | 207 | ||
| 203 | if (useThinking) { | ||
| 204 | // No prefill when thinking | ||
| 205 | voidPrefill = true; | ||
| 206 | const reasoningEffort = request.body.reasoning_effort; | 208 | const reasoningEffort = request.body.reasoning_effort; |
| 207 | const budgetTokens = calculateClaudeBudgetTokens(requestBody.max_tokens, reasoningEffort, requestBody.stream); | 209 | const budgetTokens = calculateClaudeBudgetTokens(requestBody.max_tokens, reasoningEffort, requestBody.stream); |
| 210 | |||
| 211 | if (useThinking && Number.isInteger(budgetTokens)) { | ||
| 212 | // No prefill when thinking | ||
| 213 | fixThinkingPrefill = true; | ||
| 208 | const minThinkTokens = 1024; | 214 | const minThinkTokens = 1024; |
| 209 | if (requestBody.max_tokens <= minThinkTokens) { | 215 | if (requestBody.max_tokens <= minThinkTokens) { |
| 210 | const newValue = requestBody.max_tokens + minThinkTokens; | 216 | const newValue = requestBody.max_tokens + minThinkTokens; |
| @@ -223,8 +229,8 @@ async function sendClaudeRequest(request, response) { | |||
| 223 | delete requestBody.top_k; | 229 | delete requestBody.top_k; |
| 224 | } | 230 | } |
| 225 | 231 | ||
| 226 | if (voidPrefill && convertedPrompt.messages.length && convertedPrompt.messages[convertedPrompt.messages.length - 1].role === 'assistant') { | 232 | if (fixThinkingPrefill && convertedPrompt.messages.length && convertedPrompt.messages[convertedPrompt.messages.length - 1].role === 'assistant') { |
| 227 | convertedPrompt.messages.push({ role: 'user', content: [{ type: 'text', text: '\u200b' }] }); | 233 | convertedPrompt.messages[convertedPrompt.messages.length - 1].role = 'user'; |
| 228 | } | 234 | } |
| 229 | 235 | ||
| 230 | if (betaHeaders.length) { | 236 | if (betaHeaders.length) { |
| @@ -917,19 +917,20 @@ export function cachingAtDepthForOpenRouterClaude(messages, cachingAtDepth) { | |||
| 917 | * @param {number} maxTokens Maximum tokens | 917 | * @param {number} maxTokens Maximum tokens |
| 918 | * @param {string} reasoningEffort Reasoning effort | 918 | * @param {string} reasoningEffort Reasoning effort |
| 919 | * @param {boolean} stream If streaming is enabled | 919 | * @param {boolean} stream If streaming is enabled |
| 920 | * @returns {number} Budget tokens | 920 | * @returns {number?} Budget tokens |
| 921 | */ | 921 | */ |
| 922 | export function calculateClaudeBudgetTokens(maxTokens, reasoningEffort, stream) { | 922 | export function calculateClaudeBudgetTokens(maxTokens, reasoningEffort, stream) { |
| 923 | let budgetTokens = 0; | 923 | let budgetTokens = 0; |
| 924 | 924 | ||
| 925 | switch (reasoningEffort) { | 925 | switch (reasoningEffort) { |
| 926 | case REASONING_EFFORT.auto: | ||
| 927 | return null; | ||
| 926 | case REASONING_EFFORT.min: | 928 | case REASONING_EFFORT.min: |
| 927 | budgetTokens = 1024; | 929 | budgetTokens = 1024; |
| 928 | break; | 930 | break; |
| 929 | case REASONING_EFFORT.low: | 931 | case REASONING_EFFORT.low: |
| 930 | budgetTokens = Math.floor(maxTokens * 0.1); | 932 | budgetTokens = Math.floor(maxTokens * 0.1); |
| 931 | break; | 933 | break; |
| 932 | case REASONING_EFFORT.auto: | ||
| 933 | case REASONING_EFFORT.medium: | 934 | case REASONING_EFFORT.medium: |
| 934 | budgetTokens = Math.floor(maxTokens * 0.25); | 935 | budgetTokens = Math.floor(maxTokens * 0.25); |
| 935 | break; | 936 | break; |