Simple implementation of caching at depth that should be useful for most use cases
| @@ -168,5 +168,12 @@ claude: | ||
| 168 | 168 | # (e.g {{random}} macro or lorebooks not as in-chat injections). |
| 169 | 169 | # Otherwise, you'll just waste money on cache misses. |
| 170 | 170 | enableSystemPromptCache: false |
| 171 | + # Enables caching of the message history at depth (if supported). | |
| 172 | + # https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching | |
| 173 | + # -- IMPORTANT! -- | |
| 174 | + # Use with caution. Behavior may be unpredictable and no guarantees can or will be made. | |
| 175 | + # Set to an integer to specify the desired depth. 0 (which does NOT include the prefill) | |
| 176 | + # should be ideal for most use cases. | |
| 177 | + cachingAtDepth: false | |
| 171 | 178 | # -- SERVER PLUGIN CONFIGURATION -- |
| 172 | 179 | enableServerPlugins: false |
| @@ -80,6 +80,7 @@ async function sendClaudeRequest(request, response) { | ||
| 80 | 80 | const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE); |
| 81 | 81 | const divider = '-'.repeat(process.stdout.columns); |
| 82 | 82 | const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false) && request.body.model.startsWith('claude-3'); |
| 83 | + let cachingAtDepth = getConfigValue('claude.cachingAtDepth', false) && request.body.model.startsWith('claude-3'); | |
| 83 | 84 | |
| 84 | 85 | if (!apiKey) { |
| 85 | 86 | console.log(color.red(`Claude API key is missing.\n${divider}`)); |
| @@ -138,9 +139,25 @@ async function sendClaudeRequest(request, response) { | ||
| 138 | 139 | requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral' }; |
| 139 | 140 | } |
| 140 | 141 | } |
| 141 | - if (enableSystemPromptCache) { | |
| 142 | + | |
| 143 | + if (cachingAtDepth !== false) { | |
| 144 | + // There are extremely few scenarios in which caching the prefill is a good idea, it mostly just breaks everything | |
| 145 | + const messageCount = convertedPrompt.messages.length; | |
| 146 | + cachingAtDepth += convertedPrompt.messages[messageCount - 1].role === 'assistant' ? 1 : 0; | |
| 147 | + | |
| 148 | + if (messageCount - 1 - cachingAtDepth >= 0) { | |
| 149 | + convertedPrompt.messages[messageCount - 1 - cachingAtDepth]['cache_control'] = { type: 'ephemeral' }; | |
| 150 | + } | |
| 151 | + | |
| 152 | + if (messageCount - 1 - cachingAtDepth - 2 >= 0) { | |
| 153 | + convertedPrompt.messages[messageCount - 1 - cachingAtDepth - 2]['cache_control'] = { type: 'ephemeral' }; | |
| 154 | + } | |
| 155 | + } | |
| 156 | + | |
| 157 | + if (enableSystemPromptCache || cachingAtDepth !== false) { | |
| 142 | 158 | additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31'; |
| 143 | 159 | } |
| 160 | + | |
| 144 | 161 | console.log('Claude request:', requestBody); |
| 145 | 162 | |
| 146 | 163 | const generateResponse = await fetch(apiUrl + '/messages', { |