Simple implementation of caching at depth that should be useful for most use cases

73dabd8905c9eecf60c1aeedabd9bcfe745a2975

Honey Tree <kalakanlogs@proton.me>

2 files changed, +25 -1Showing whitespace changes
default/config.yaml+7 -0
@@ -168,5 +168,12 @@ claude:
168 # (e.g {{random}} macro or lorebooks not as in-chat injections).168 # (e.g {{random}} macro or lorebooks not as in-chat injections).
169 # Otherwise, you'll just waste money on cache misses.169 # Otherwise, you'll just waste money on cache misses.
170 enableSystemPromptCache: false170 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# -- SERVER PLUGIN CONFIGURATION --178# -- SERVER PLUGIN CONFIGURATION --
172enableServerPlugins: false179enableServerPlugins: false
src/endpoints/backends/chat-completions.js+18 -1
@@ -80,6 +80,7 @@ async function sendClaudeRequest(request, response) {
80 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE);80 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE);
81 const divider = '-'.repeat(process.stdout.columns);81 const divider = '-'.repeat(process.stdout.columns);
82 const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false) && request.body.model.startsWith('claude-3');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');
8384
84 if (!apiKey) {85 if (!apiKey) {
85 console.log(color.red(`Claude API key is missing.\n${divider}`));86 console.log(color.red(`Claude API key is missing.\n${divider}`));
@@ -138,9 +139,25 @@ async function sendClaudeRequest(request, response) {
138 requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral' };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 additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31';158 additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31';
143 }159 }
160
144 console.log('Claude request:', requestBody);161 console.log('Claude request:', requestBody);
145162
146 const generateResponse = await fetch(apiUrl + '/messages', {163 const generateResponse = await fetch(apiUrl + '/messages', {