@@ -57,6 +57,54 @@ static char *ollama_generate_prompt_from_messages(const RList *msgs, char **syst
5757 return prompt ;
5858}
5959
60+ static void cache_part (RStrBuf * sb , const char * s ) {
61+ const char * v = r_str_get (s );
62+ r_strbuf_appendf (sb , "%zu:%s\n" , strlen (v ), v );
63+ }
64+
65+ static char * cache_prefix (const char * provider , const char * model , const char * tools_json , const char * messages_json ) {
66+ RStrBuf * sb = r_strbuf_new ("" );
67+ cache_part (sb , provider );
68+ cache_part (sb , model );
69+ cache_part (sb , tools_json );
70+ const char * messages = r_str_get (messages_json );
71+ size_t len = strlen (messages );
72+ if (len > 0 && messages [len - 1 ] == ']' ) {
73+ len -- ;
74+ }
75+ r_strbuf_appendf (sb , "%zu:" , len );
76+ r_strbuf_append_n (sb , messages , len );
77+ return r_strbuf_drain (sb );
78+ }
79+
80+ static size_t prefix_len (const char * a , const char * b ) {
81+ size_t i = 0 ;
82+ while (a [i ] && b [i ] && a [i ] == b [i ]) {
83+ i ++ ;
84+ }
85+ return i ;
86+ }
87+
88+ static void check_chat_cache (RCorePluginSession * cps , const char * provider , const char * model , const char * tools_json , const char * messages_json ) {
89+ RCore * core = cps -> core ;
90+ R2AI_State * state = cps -> data ;
91+ if (!state || !r_config_get_b (core -> config , "r2ai.cacheck" )) {
92+ return ;
93+ }
94+ char * prefix = cache_prefix (provider , model , tools_json , messages_json );
95+ if (state -> cache_prefix ) {
96+ const size_t old_len = strlen (state -> cache_prefix );
97+ if (!r_str_startswith (prefix , state -> cache_prefix )) {
98+ const size_t kept = prefix_len (state -> cache_prefix , prefix );
99+ R_LOG_WARN ("Chat cache prefix changed: preserved %zu/%zu bytes from previous request. Keep system prompt, tool catalog, and previous messages append-only to maximize provider cache hits." , kept , old_len );
100+ } else {
101+ R_LOG_DEBUG ("Chat cache prefix preserved: %zu bytes" , old_len );
102+ }
103+ R_FREE (state -> cache_prefix );
104+ }
105+ state -> cache_prefix = prefix ;
106+ }
107+
60108R_IPI R2AI_ChatResponse * r2ai_openai (RCorePluginSession * cps , R2AIArgs args ) {
61109 RCore * core = cps -> core ;
62110 const char * provider_name = R_STR_ISNOTEMPTY (args .provider )
@@ -229,6 +277,9 @@ R_IPI R2AI_ChatResponse *r2ai_openai(RCorePluginSession *cps, R2AIArgs args) {
229277 pj_end (pj );
230278
231279 char * complete_json = pj_drain (pj );
280+ if (!use_generate ) {
281+ check_chat_cache (cps , provider_name , model_name , openai_tools_json , chat_messages_json );
282+ }
232283 free (chat_messages_json );
233284 free (generate_prompt );
234285 free (generate_system );
0 commit comments