Moved most of code to prompt converters
| @@ -26,6 +26,8 @@ import { | ||
| 26 | 26 | convertMistralMessages, |
| 27 | 27 | convertAI21Messages, |
| 28 | 28 | mergeMessages, |
| 29 | + cachingAtDepthForOpenRouterClaude, | |
| 30 | + cachingAtDepthForClaude, | |
| 29 | 31 | } from '../../prompt-converters.js'; |
| 30 | 32 | |
| 31 | 33 | import { readSecret, SECRET_KEYS } from '../secrets.js'; |
| @@ -145,19 +147,7 @@ async function sendClaudeRequest(request, response) { | ||
| 145 | 147 | } |
| 146 | 148 | |
| 147 | 149 | if (cachingAtDepth !== -1) { |
| 148 | - // There are extremely few scenarios in which caching the prefill is a good idea, it mostly just breaks everything | |
| 150 | + cachingAtDepthForClaude(convertedPrompt.messages, cachingAtDepth); | |
| 149 | - const messageCount = convertedPrompt.messages.length; | |
| 150 | - cachingAtDepth += convertedPrompt.messages[messageCount - 1].role === 'assistant' ? 1 : 0; | |
| 151 | - | |
| 152 | - if (messageCount - 1 - cachingAtDepth >= 0) { | |
| 153 | - const contentCount = convertedPrompt.messages[messageCount - 1 - cachingAtDepth].content.length; | |
| 154 | - convertedPrompt.messages[messageCount - 1 - cachingAtDepth].content[contentCount - 1]['cache_control'] = { type: 'ephemeral' }; | |
| 155 | - } | |
| 156 | - | |
| 157 | - if (messageCount - 1 - cachingAtDepth - 2 >= 0) { | |
| 158 | - const contentCount = convertedPrompt.messages[messageCount - 1 - cachingAtDepth].content.length; | |
| 159 | - convertedPrompt.messages[messageCount - 1 - cachingAtDepth - 2].content[contentCount - 1]['cache_control'] = { type: 'ephemeral' }; | |
| 160 | - } | |
| 161 | 151 | } |
| 162 | 152 | |
| 163 | 153 | if (enableSystemPromptCache || cachingAtDepth !== -1) { |
| @@ -902,43 +892,7 @@ router.post('/generate', jsonParser, function (request, response) { | ||
| 902 | 892 | |
| 903 | 893 | let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1); |
| 904 | 894 | if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model.startsWith('anthropic/claude-3')) { |
| 905 | - //caching the prefill is a terrible idea in general | |
| 895 | + cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth); | |
| 906 | - let passedThePrefill = false; | |
| 907 | - //depth here is the number of message role switches | |
| 908 | - let depth = 0; | |
| 909 | - let previousRoleName = ""; | |
| 910 | - for (let i = request.body.messages.length - 1; i >= 0; i--) { | |
| 911 | - if (!passedThePrefill && request.body.messages[i].role === 'assistant') { | |
| 912 | - continue; | |
| 913 | - } | |
| 914 | - | |
| 915 | - passedThePrefill = true; | |
| 916 | - | |
| 917 | - if (request.body.messages[i].role !== previousRoleName) { | |
| 918 | - if (depth === cachingAtDepth || depth === cachingAtDepth + 2) { | |
| 919 | - const content = request.body.messages[i].content; | |
| 920 | - if (typeof content === 'string') { | |
| 921 | - request.body.messages[i].content = [{ | |
| 922 | - type: 'text', | |
| 923 | - text: content, | |
| 924 | - cache_control: { type: "ephemeral"}, | |
| 925 | - }]; | |
| 926 | - } else { | |
| 927 | - const contentPartCount = content.length; | |
| 928 | - content[contentPartCount - 1].cache_control = { | |
| 929 | - type: "ephemeral" | |
| 930 | - } | |
| 931 | - } | |
| 932 | - } | |
| 933 | - | |
| 934 | - if (depth === cachingAtDepth + 2) { | |
| 935 | - break | |
| 936 | - } | |
| 937 | - | |
| 938 | - depth += 1; | |
| 939 | - previousRoleName = request.body.messages[i].role; | |
| 940 | - } | |
| 941 | - } | |
| 942 | 896 | } |
| 943 | 897 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) { |
| 944 | 898 | apiUrl = request.body.custom_url; |
| @@ -718,3 +718,77 @@ export function convertTextCompletionPrompt(messages) { | ||
| 718 | 718 | }); |
| 719 | 719 | return messageStrings.join('\n') + '\nassistant:'; |
| 720 | 720 | } |
| 721 | + | |
| 722 | +export function cachingAtDepthForClaude(messages, cachingAtDepth) { | |
| 723 | + let passedThePrefill = false; | |
| 724 | + let depth = 0; | |
| 725 | + let previousRoleName = ""; | |
| 726 | + | |
| 727 | + for (let i = messages.length - 1; i >= 0; i--) { | |
| 728 | + if (!passedThePrefill && messages[i].role === 'assistant') { | |
| 729 | + continue; | |
| 730 | + } | |
| 731 | + | |
| 732 | + passedThePrefill = true; | |
| 733 | + | |
| 734 | + if (messages[i].role !== previousRoleName) { | |
| 735 | + if (depth === cachingAtDepth || depth === cachingAtDepth + 2) { | |
| 736 | + const content = messages[i].content; | |
| 737 | + content[content.length - 1].cache_control = {type: "ephemeral"}; | |
| 738 | + } | |
| 739 | + | |
| 740 | + if (depth === cachingAtDepth + 2) { | |
| 741 | + break; | |
| 742 | + } | |
| 743 | + | |
| 744 | + depth += 1; | |
| 745 | + previousRoleName = messages[i].role; | |
| 746 | + } | |
| 747 | + } | |
| 748 | +} | |
| 749 | + | |
| 750 | +/** | |
| 751 | + * Append cache_control headers to an OpenRouter request at depth. Directly modifies the | |
| 752 | + * messages array. | |
| 753 | + * @param {object[]} messages Array of messages | |
| 754 | + * @param {number} cachingAtDepth Depth at which caching is supposed to occur | |
| 755 | + */ | |
| 756 | +export function cachingAtDepthForOpenRouterClaude(messages, cachingAtDepth) { | |
| 757 | + //caching the prefill is a terrible idea in general | |
| 758 | + let passedThePrefill = false; | |
| 759 | + //depth here is the number of message role switches | |
| 760 | + let depth = 0; | |
| 761 | + let previousRoleName = ""; | |
| 762 | + for (let i = messages.length - 1; i >= 0; i--) { | |
| 763 | + if (!passedThePrefill && messages[i].role === 'assistant') { | |
| 764 | + continue; | |
| 765 | + } | |
| 766 | + | |
| 767 | + passedThePrefill = true; | |
| 768 | + | |
| 769 | + if (messages[i].role !== previousRoleName) { | |
| 770 | + if (depth === cachingAtDepth || depth === cachingAtDepth + 2) { | |
| 771 | + const content = messages[i].content; | |
| 772 | + if (typeof content === 'string') { | |
| 773 | + messages[i].content = [{ | |
| 774 | + type: 'text', | |
| 775 | + text: content, | |
| 776 | + cache_control: {type: "ephemeral"}, | |
| 777 | + }]; | |
| 778 | + } else { | |
| 779 | + const contentPartCount = content.length; | |
| 780 | + content[contentPartCount - 1].cache_control = { | |
| 781 | + type: "ephemeral" | |
| 782 | + } | |
| 783 | + } | |
| 784 | + } | |
| 785 | + | |
| 786 | + if (depth === cachingAtDepth + 2) { | |
| 787 | + break | |
| 788 | + } | |
| 789 | + | |
| 790 | + depth += 1; | |
| 791 | + previousRoleName = messages[i].role; | |
| 792 | + } | |
| 793 | + } | |
| 794 | +} | |