Adding Claude caching support to OpenRouter as well

befe5a7171e62517cb6cd985a37eafb9a58f1d11

Honey Tree <kalakanlogs@proton.me>

1 files changed, +44 -3Ignore whitespace
src/endpoints/backends/chat-completions.js+44 -3
@@ -80,9 +80,9 @@ async function sendClaudeRequest(request, response) {
8080 const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.CLAUDE);
8181 const divider = '-'.repeat(process.stdout.columns);
8282 const enableSystemPromptCache = getConfigValue('claude.enableSystemPromptCache', false) && request.body.model.startsWith('claude-3');
8383 let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1) && request.body.model.startsWith('claude-3');
8484 // Disabled if not an integer or negative, or if the model doesn't support it
8585 if (!Number.isInteger(cachingAtDepth) || cachingAtDepth < 0 || !request.body.model.startsWith('claude-3')) {
8686 cachingAtDepth = -1;
8787 }
8888
@@ -899,6 +899,47 @@ router.post('/generate', jsonParser, function (request, response) {
899899 if (request.body.use_fallback) {
900900 bodyParams['route'] = 'fallback';
901901 }
902+
903+ let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1);
904+ if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model.startsWith('anthropic/claude-3')) {
905+ //caching the prefill is a terrible idea in general
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+ }
902943 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) {
903944 apiUrl = request.body.custom_url;
904945 apiKey = readSecret(request.user.directories, SECRET_KEYS.CUSTOM);