Add optional Claude system prompt cache.
| @@ -41,6 +41,11 @@ enableCorsProxy: false | ||
| 41 | 41 | enableUserAccounts: false |
| 42 | 42 | # Enable discreet login mode: hides user list on the login screen |
| 43 | 43 | enableDiscreetLogin: false |
| 44 | +# User session timeout *in seconds* (defaults to 24 hours). | |
| 45 | +## Set to a positive number to expire session after a certain time of inactivity | |
| 46 | +## Set to 0 to expire session when the browser is closed | |
| 47 | +## Set to a negative number to disable session expiration | |
| 48 | +sessionTimeout: 86400 | |
| 44 | 49 | # Used to sign session cookies. Will be auto-generated if not set |
| 45 | 50 | cookieSecret: '' |
| 46 | 51 | # Disable CSRF protection - NOT RECOMMENDED |
| @@ -123,10 +128,14 @@ ollama: | ||
| 123 | 128 | # * 0: Unload the model immediately after the request |
| 124 | 129 | # * 5m: Keep the model loaded for 5 minutes after the request. Accepts duration strings (e.g. 5h30m40s) |
| 125 | 130 | keepAlive: -1 |
| 131 | +# -- ANTHROPIC CLAUDE API CONFIGURATION -- | |
| 132 | +claude: | |
| 133 | + # Enables caching of the system prompt (if supported). | |
| 134 | + # https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching | |
| 135 | + # -- IMPORTANT! -- | |
| 136 | + # Use only when the prompt before the chat history is static and doesn't change between requests | |
| 137 | + # (e.g {{random}} macro or lorebooks not as in-chat injections). | |
| 138 | + # Otherwise, you'll just waste money on cache misses. | |
| 139 | + enableSystemPromptCache: false | |
| 126 | 140 | # -- SERVER PLUGIN CONFIGURATION -- |
| 127 | 141 | enableServerPlugins: false |
| 128 | -# User session timeout *in seconds* (defaults to 24 hours). | |
| 129 | -## Set to a positive number to expire session after a certain time of inactivity | |
| 130 | -## Set to 0 to expire session when the browser is closed | |
| 131 | -## Set to a negative number to disable session expiration | |
| 132 | -sessionTimeout: 86400 | |
| @@ -105,6 +105,7 @@ async function sendClaudeRequest(request, response) { | ||
| 105 | 105 | const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString(); |
| 106 | 106 | const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE); |
| 107 | 107 | const divider = '-'.repeat(process.stdout.columns); |
| 108 | + const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false); | |
| 108 | 109 | |
| 109 | 110 | if (!apiKey) { |
| 110 | 111 | console.log(color.red(`Claude API key is missing.\n${divider}`)); |
| @@ -118,8 +119,8 @@ async function sendClaudeRequest(request, response) { | ||
| 118 | 119 | controller.abort(); |
| 119 | 120 | }); |
| 120 | 121 | const additionalHeaders = {}; |
| 121 | 122 | letconst use_system_promptuseSystemPrompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt; |
| 122 | 123 | letconst converted_promptconvertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, use_system_promptuseSystemPrompt, request.body.human_sysprompt_message, request.body.char_name, request.body.user_name); |
| 123 | 124 | // Add custom stop sequences |
| 124 | 125 | const stopSequences = []; |
| 125 | 126 | if (Array.isArray(request.body.stop)) { |
| @@ -127,7 +128,7 @@ async function sendClaudeRequest(request, response) { | ||
| 127 | 128 | } |
| 128 | 129 | |
| 129 | 130 | const requestBody = { |
| 130 | 131 | messages: converted_promptconvertedPrompt.messages, |
| 131 | 132 | model: request.body.model, |
| 132 | 133 | max_tokens: request.body.max_tokens, |
| 133 | 134 | stop_sequences: stopSequences, |
| @@ -136,13 +137,15 @@ async function sendClaudeRequest(request, response) { | ||
| 136 | 137 | top_k: request.body.top_k, |
| 137 | 138 | stream: request.body.stream, |
| 138 | 139 | }; |
| 139 | 140 | if (use_system_promptuseSystemPrompt) { |
| 140 | 141 | requestBody.system = converted_prompt.systemPrompt;enableSystemPromptCache |
| 142 | + ? [{ type: 'text', text: convertedPrompt.systemPrompt, cache_control: { type: 'ephemeral' } }] | |
| 143 | + : convertedPrompt.systemPrompt; | |
| 141 | 144 | } |
| 142 | 145 | if (Array.isArray(request.body.tools) && request.body.tools.length > 0) { |
| 143 | 146 | // Claude doesn't do prefills on function calls, and doesn't allow empty messages |
| 144 | 147 | if (converted_promptconvertedPrompt.messages.length && converted_promptconvertedPrompt.messages[converted_promptconvertedPrompt.messages.length - 1].role === 'assistant') { |
| 145 | 148 | converted_promptconvertedPrompt.messages.push({ role: 'user', content: '.' }); |
| 146 | 149 | } |
| 147 | 150 | additionalHeaders['anthropic-beta'] = 'tools-2024-05-16'; |
| 148 | 151 | requestBody.tool_choice = { type: request.body.tool_choice === 'required' ? 'any' : 'auto' }; |
| @@ -151,6 +154,9 @@ async function sendClaudeRequest(request, response) { | ||
| 151 | 154 | .map(tool => tool.function) |
| 152 | 155 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); |
| 153 | 156 | } |
| 157 | + if (enableSystemPromptCache) { | |
| 158 | + additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31'; | |
| 159 | + } | |
| 154 | 160 | console.log('Claude request:', requestBody); |
| 155 | 161 | |
| 156 | 162 | const generateResponse = await fetch(apiUrl + '/messages', { |