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 {
2626 convertMistralMessages,
2727 convertAI21Messages,
2828 mergeMessages,
29+ cachingAtDepthForOpenRouterClaude,
30+ cachingAtDepthForClaude,
2931} from '../../prompt-converters.js';
3032
3133import { readSecret, SECRET_KEYS } from '../secrets.js';
@@ -145,19 +147,7 @@ async function sendClaudeRequest(request, response) {
145147 }
146148
147149 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- }
161151 }
162152
163153 if (enableSystemPromptCache || cachingAtDepth !== -1) {
@@ -902,43 +892,7 @@ router.post('/generate', jsonParser, function (request, response) {
902892
903893 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1);
904894 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- }
942896 }
943897 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) {
944898 apiUrl = request.body.custom_url;
src/prompt-converters.js+74 -0
@@ -718,3 +718,77 @@ export function convertTextCompletionPrompt(messages) {
718718 });
719719 return messageStrings.join('\n') + '\nassistant:';
720720}
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+}