Moved most of code to prompt converters

c3caa1699e6182b3108a24344032ee89db5b26e0

Honey Tree <kalakanlogs@proton.me>

2 files changed, +78 -50Showing whitespace changes
src/endpoints/backends/chat-completions.js+4 -50
@@ -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';
3032
31import { readSecret, SECRET_KEYS } from '../secrets.js';33import { readSecret, SECRET_KEYS } from '../secrets.js';
@@ -145,19 +147,7 @@ async function sendClaudeRequest(request, response) {
145 }147 }
146148
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 everything150 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 }
162152
163 if (enableSystemPromptCache || cachingAtDepth !== -1) {153 if (enableSystemPromptCache || cachingAtDepth !== -1) {
@@ -902,43 +892,7 @@ router.post('/generate', jsonParser, function (request, response) {
902892
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 general895 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;
src/prompt-converters.js+74 -0
@@ -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
722export 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 */
756export 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}