Claude: control cache TTL with config
| @@ -234,6 +234,10 @@ claude: | |||
| 234 | # should be ideal for most use cases. | 234 | # should be ideal for most use cases. |
| 235 | # Any value other than a non-negative integer will be ignored and caching at depth will not be enabled. | 235 | # Any value other than a non-negative integer will be ignored and caching at depth will not be enabled. |
| 236 | cachingAtDepth: -1 | 236 | cachingAtDepth: -1 |
| 237 | # Use 1h TTL instead of the default 5m. | ||
| 238 | ## 5m: base price x 1.25 | ||
| 239 | ## 1h: base price x 2 | ||
| 240 | extendedTTL: false | ||
| 237 | # -- GOOGLE GEMINI API CONFIGURATION -- | 241 | # -- GOOGLE GEMINI API CONFIGURATION -- |
| 238 | gemini: | 242 | gemini: |
| 239 | # API endpoint version ("v1beta" or "v1alpha") | 243 | # API endpoint version ("v1beta" or "v1alpha") |
| @@ -154,6 +154,7 @@ async function sendClaudeRequest(request, response) { | |||
| 154 | const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, getPromptNames(request)); | 154 | const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, getPromptNames(request)); |
| 155 | const useThinking = /^claude-(3-7|opus-4|sonnet-4)/.test(request.body.model) && Boolean(request.body.include_reasoning); | 155 | const useThinking = /^claude-(3-7|opus-4|sonnet-4)/.test(request.body.model) && Boolean(request.body.include_reasoning); |
| 156 | const useWebSearch = /^claude-(3-5|3-7|opus-4|sonnet-4)/.test(request.body.model) && Boolean(request.body.enable_web_search); | 156 | const useWebSearch = /^claude-(3-5|3-7|opus-4|sonnet-4)/.test(request.body.model) && Boolean(request.body.enable_web_search); |
| 157 | const cacheTTL = getConfigValue('claude.extendedTTL', false, 'boolean') ? '1h' : '5m'; | ||
| 157 | let fixThinkingPrefill = false; | 158 | let fixThinkingPrefill = false; |
| 158 | // Add custom stop sequences | 159 | // Add custom stop sequences |
| 159 | const stopSequences = []; | 160 | const stopSequences = []; |
| @@ -174,7 +175,7 @@ async function sendClaudeRequest(request, response) { | |||
| 174 | }; | 175 | }; |
| 175 | if (useSystemPrompt) { | 176 | if (useSystemPrompt) { |
| 176 | if (enableSystemPromptCache && Array.isArray(convertedPrompt.systemPrompt) && convertedPrompt.systemPrompt.length) { | 177 | if (enableSystemPromptCache && Array.isArray(convertedPrompt.systemPrompt) && convertedPrompt.systemPrompt.length) { |
| 177 | convertedPrompt.systemPrompt[convertedPrompt.systemPrompt.length - 1]['cache_control'] = { type: 'ephemeral', ttl: '1h' }; | 178 | convertedPrompt.systemPrompt[convertedPrompt.systemPrompt.length - 1]['cache_control'] = { type: 'ephemeral', ttl: cacheTTL }; |
| 178 | } | 179 | } |
| 179 | 180 | ||
| 180 | requestBody.system = convertedPrompt.systemPrompt; | 181 | requestBody.system = convertedPrompt.systemPrompt; |
| @@ -190,7 +191,7 @@ async function sendClaudeRequest(request, response) { | |||
| 190 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); | 191 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); |
| 191 | 192 | ||
| 192 | if (enableSystemPromptCache && requestBody.tools.length) { | 193 | if (enableSystemPromptCache && requestBody.tools.length) { |
| 193 | requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral', ttl: '1h' }; | 194 | requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral', ttl: cacheTTL }; |
| 194 | } | 195 | } |
| 195 | } | 196 | } |
| 196 | 197 | ||
| @@ -203,7 +204,7 @@ async function sendClaudeRequest(request, response) { | |||
| 203 | } | 204 | } |
| 204 | 205 | ||
| 205 | if (cachingAtDepth !== -1) { | 206 | if (cachingAtDepth !== -1) { |
| 206 | cachingAtDepthForClaude(convertedPrompt.messages, cachingAtDepth); | 207 | cachingAtDepthForClaude(convertedPrompt.messages, cachingAtDepth, cacheTTL); |
| 207 | } | 208 | } |
| 208 | 209 | ||
| 209 | if (enableSystemPromptCache || cachingAtDepth !== -1) { | 210 | if (enableSystemPromptCache || cachingAtDepth !== -1) { |
| @@ -854,7 +854,13 @@ export function convertTextCompletionPrompt(messages) { | |||
| 854 | return messageStrings.join('\n') + '\nassistant:'; | 854 | return messageStrings.join('\n') + '\nassistant:'; |
| 855 | } | 855 | } |
| 856 | 856 | ||
| 857 | export function cachingAtDepthForClaude(messages, cachingAtDepth) { | 857 | /** |
| 858 | * Append cache_control object to a Claude messages at depth. Directly modifies the messages array. | ||
| 859 | * @param {any[]} messages Messages to modify | ||
| 860 | * @param {number} cachingAtDepth Depth at which caching is supposed to occur | ||
| 861 | * @param {string} ttl TTL value | ||
| 862 | */ | ||
| 863 | export function cachingAtDepthForClaude(messages, cachingAtDepth, ttl) { | ||
| 858 | let passedThePrefill = false; | 864 | let passedThePrefill = false; |
| 859 | let depth = 0; | 865 | let depth = 0; |
| 860 | let previousRoleName = ''; | 866 | let previousRoleName = ''; |
| @@ -869,7 +875,7 @@ export function cachingAtDepthForClaude(messages, cachingAtDepth) { | |||
| 869 | if (messages[i].role !== previousRoleName) { | 875 | if (messages[i].role !== previousRoleName) { |
| 870 | if (depth === cachingAtDepth || depth === cachingAtDepth + 2) { | 876 | if (depth === cachingAtDepth || depth === cachingAtDepth + 2) { |
| 871 | const content = messages[i].content; | 877 | const content = messages[i].content; |
| 872 | content[content.length - 1].cache_control = { type: 'ephemeral', ttl: '1h' }; | 878 | content[content.length - 1].cache_control = { type: 'ephemeral', ttl: ttl }; |
| 873 | } | 879 | } |
| 874 | 880 | ||
| 875 | if (depth === cachingAtDepth + 2) { | 881 | if (depth === cachingAtDepth + 2) { |