Moved most of code to prompt converters
| @@ -26,6 +26,8 @@ import { | |||
| 26 | convertMistralMessages, | 26 | convertMistralMessages, |
| 27 | convertAI21Messages, | 27 | convertAI21Messages, |
| 28 | mergeMessages, | 28 | mergeMessages, |
| 29 | cachingAtDepthForOpenRouterClaude, | ||
| 30 | cachingAtDepthForClaude, | ||
| 29 | } from '../../prompt-converters.js'; | 31 | } from '../../prompt-converters.js'; |
| 30 | 32 | ||
| 31 | import { readSecret, SECRET_KEYS } from '../secrets.js'; | 33 | import { readSecret, SECRET_KEYS } from '../secrets.js'; |
| @@ -145,19 +147,7 @@ async function sendClaudeRequest(request, response) { | |||
| 145 | } | 147 | } |
| 146 | 148 | ||
| 147 | if (cachingAtDepth !== -1) { | 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 | if (enableSystemPromptCache || cachingAtDepth !== -1) { | 153 | if (enableSystemPromptCache || cachingAtDepth !== -1) { |
| @@ -902,43 +892,7 @@ router.post('/generate', jsonParser, function (request, response) { | |||
| 902 | 892 | ||
| 903 | let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1); | 893 | let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1); |
| 904 | if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model.startsWith('anthropic/claude-3')) { | 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 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) { | 897 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) { |
| 944 | apiUrl = request.body.custom_url; | 898 | apiUrl = request.body.custom_url; |
| @@ -718,3 +718,77 @@ export function convertTextCompletionPrompt(messages) { | |||
| 718 | }); | 718 | }); |
| 719 | return messageStrings.join('\n') + '\nassistant:'; | 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 | } | ||