Claude: Use multi-part system prompt, cache tools
| @@ -87,7 +87,7 @@ async function sendClaudeRequest(request, response) { | |||
| 87 | const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString(); | 87 | const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString(); |
| 88 | const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE); | 88 | const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE); |
| 89 | const divider = '-'.repeat(process.stdout.columns); | 89 | const divider = '-'.repeat(process.stdout.columns); |
| 90 | const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false); | 90 | const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false) && request.body.model.startsWith('claude-3'); |
| 91 | 91 | ||
| 92 | if (!apiKey) { | 92 | if (!apiKey) { |
| 93 | console.log(color.red(`Claude API key is missing.\n${divider}`)); | 93 | console.log(color.red(`Claude API key is missing.\n${divider}`)); |
| @@ -110,7 +110,7 @@ async function sendClaudeRequest(request, response) { | |||
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | const requestBody = { | 112 | const requestBody = { |
| 113 | /** @type {any} */ system: '', | 113 | /** @type {any} */ system: [], |
| 114 | messages: convertedPrompt.messages, | 114 | messages: convertedPrompt.messages, |
| 115 | model: request.body.model, | 115 | model: request.body.model, |
| 116 | max_tokens: request.body.max_tokens, | 116 | max_tokens: request.body.max_tokens, |
| @@ -121,9 +121,11 @@ async function sendClaudeRequest(request, response) { | |||
| 121 | stream: request.body.stream, | 121 | stream: request.body.stream, |
| 122 | }; | 122 | }; |
| 123 | if (useSystemPrompt) { | 123 | if (useSystemPrompt) { |
| 124 | requestBody.system = enableSystemPromptCache | 124 | if (enableSystemPromptCache && Array.isArray(convertedPrompt.systemPrompt) && convertedPrompt.systemPrompt.length) { |
| 125 | ? [{ type: 'text', text: convertedPrompt.systemPrompt, cache_control: { type: 'ephemeral' } }] | 125 | convertedPrompt.systemPrompt[convertedPrompt.systemPrompt.length - 1]['cache_control'] = { type: 'ephemeral' }; |
| 126 | : convertedPrompt.systemPrompt; | 126 | } |
| 127 | |||
| 128 | requestBody.system = convertedPrompt.systemPrompt; | ||
| 127 | } else { | 129 | } else { |
| 128 | delete requestBody.system; | 130 | delete requestBody.system; |
| 129 | } | 131 | } |
| @@ -138,6 +140,10 @@ async function sendClaudeRequest(request, response) { | |||
| 138 | .filter(tool => tool.type === 'function') | 140 | .filter(tool => tool.type === 'function') |
| 139 | .map(tool => tool.function) | 141 | .map(tool => tool.function) |
| 140 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); | 142 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); |
| 143 | |||
| 144 | if (enableSystemPromptCache && requestBody.tools.length) { | ||
| 145 | requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral' }; | ||
| 146 | } | ||
| 141 | } | 147 | } |
| 142 | if (enableSystemPromptCache) { | 148 | if (enableSystemPromptCache) { |
| 143 | additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31'; | 149 | additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31'; |
| @@ -95,7 +95,7 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, | |||
| 95 | * @param {string} userName User name | 95 | * @param {string} userName User name |
| 96 | */ | 96 | */ |
| 97 | function convertClaudeMessages(messages, prefillString, useSysPrompt, humanMsgFix, charName = '', userName = '') { | 97 | function convertClaudeMessages(messages, prefillString, useSysPrompt, humanMsgFix, charName = '', userName = '') { |
| 98 | let systemPrompt = ''; | 98 | let systemPrompt = []; |
| 99 | if (useSysPrompt) { | 99 | if (useSysPrompt) { |
| 100 | // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array. | 100 | // Collect all the system messages up until the first instance of a non-system message, and then remove them from the messages array. |
| 101 | let i; | 101 | let i; |
| @@ -114,7 +114,7 @@ function convertClaudeMessages(messages, prefillString, useSysPrompt, humanMsgFi | |||
| 114 | messages[i].content = `${charName}: ${messages[i].content}`; | 114 | messages[i].content = `${charName}: ${messages[i].content}`; |
| 115 | } | 115 | } |
| 116 | } | 116 | } |
| 117 | systemPrompt += `${messages[i].content}\n\n`; | 117 | systemPrompt.push({ type: 'text', text: messages[i].content }); |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | messages.splice(0, i); | 120 | messages.splice(0, i); |
| @@ -246,7 +246,7 @@ function convertClaudeMessages(messages, prefillString, useSysPrompt, humanMsgFi | |||
| 246 | } | 246 | } |
| 247 | }); | 247 | }); |
| 248 | 248 | ||
| 249 | return { messages: mergedMessages, systemPrompt: systemPrompt.trim() }; | 249 | return { messages: mergedMessages, systemPrompt: systemPrompt }; |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | /** | 252 | /** |