@@ -21,7 +21,7 @@ export async function createOpenRouterStream(
2121
2222 // prompt caching: https://openrouter.ai/docs/prompt-caching
2323 // this was initially specifically for claude models (some models may 'support prompt caching' automatically without this)
24- // gemini models only use the last breakpoint for caching, so the others will be ignored
24+ // includes custom support for gemini which does not have iterative caching
2525 switch ( model . id ) {
2626 case "anthropic/claude-3.7-sonnet" :
2727 case "anthropic/claude-3.7-sonnet:beta" :
@@ -40,10 +40,6 @@ export async function createOpenRouterStream(
4040 case "anthropic/claude-3-haiku:beta" :
4141 case "anthropic/claude-3-opus" :
4242 case "anthropic/claude-3-opus:beta" :
43- case "google/gemini-2.5-pro-preview-03-25" :
44- case "google/gemini-2.0-flash-001" :
45- case "google/gemini-flash-1.5" :
46- case "google/gemini-pro-1.5" :
4743 openAiMessages [ 0 ] = {
4844 role : "system" ,
4945 content : [
@@ -75,6 +71,52 @@ export async function createOpenRouterStream(
7571 }
7672 } )
7773 break
74+ case "google/gemini-2.5-pro-preview-03-25" :
75+ case "google/gemini-2.0-flash-001" :
76+ case "google/gemini-flash-1.5" :
77+ case "google/gemini-pro-1.5" :
78+ // gemini only uses the last breakpoint for caching, so the others will be ignored
79+ openAiMessages [ 0 ] = {
80+ role : "system" ,
81+ content : [
82+ {
83+ type : "text" ,
84+ text : systemPrompt ,
85+ // @ts -ignore-next-line
86+ cache_control : { type : "ephemeral" } ,
87+ } ,
88+ ] ,
89+ }
90+
91+ const GEMINI_CACHE_USER_MESSAGE_INTERVAL = 4 // add new breakpoint every 4 turns
92+ const userMessages = openAiMessages . filter ( ( msg ) => msg . role === "user" )
93+
94+ const userMessageCount = userMessages . length
95+ const targetUserMessageNumber =
96+ Math . floor ( userMessageCount / GEMINI_CACHE_USER_MESSAGE_INTERVAL ) * GEMINI_CACHE_USER_MESSAGE_INTERVAL
97+
98+ if ( targetUserMessageNumber > 0 ) {
99+ // otherwise dont need to add a breakpoint
100+ const msg = userMessages [ targetUserMessageNumber - 1 ]
101+
102+ if ( msg ) {
103+ if ( typeof msg . content === "string" ) {
104+ msg . content = [ { type : "text" , text : msg . content } ]
105+ }
106+ if ( Array . isArray ( msg . content ) ) {
107+ // NOTE: this is fine since env details will always be added at the end. but if it weren't there, and the user added a image_url type message, it would pop a text part before it and then move it after to the end.
108+ let lastTextPart = msg . content . filter ( ( part ) => part . type === "text" ) . pop ( )
109+
110+ if ( ! lastTextPart ) {
111+ lastTextPart = { type : "text" , text : "..." }
112+ msg . content . push ( lastTextPart )
113+ }
114+ // @ts -ignore-next-line
115+ lastTextPart [ "cache_control" ] = { type : "ephemeral" }
116+ }
117+ }
118+ }
119+ break
78120 default :
79121 break
80122 }
0 commit comments