50
50
ABSOLUTE_MAX_CONTEXT_TOKENS = 1024 * 200
51
51
DEFAULT_MAX_CONTEXT_TOKENS = ABSOLUTE_MAX_CONTEXT_TOKENS
52
52
53
+ LIMIT_WINDOW = 60.0
54
+
53
55
SETTINGS_FILE = 'nvim_claude.json'
54
56
55
57
DEFAULT_SYSTEM_PROMPT = """
@@ -135,6 +137,7 @@ def __init__(self, nvim):
135
137
"system_prompt" : DEFAULT_SYSTEM_PROMPT ,
136
138
"temperature" : DEFAULT_TEMPERATURE ,
137
139
"timeout" : DEFAULT_TIMEOUT ,
140
+ "limit_window" : LIMIT_WINDOW ,
138
141
}
139
142
140
143
# Load configuration from file
@@ -163,6 +166,7 @@ def __init__(self, nvim):
163
166
self .system_prompt = config ["system_prompt" ]
164
167
self .temperature = config ["temperature" ]
165
168
self .timeout = config ["timeout" ]
169
+ self .limit_window = config ["limit_window" ]
166
170
self .claude_client = anthropic .Client (
167
171
timeout = Timeout (10.0 , read = self .timeout , write = self .timeout ))
168
172
@@ -176,6 +180,7 @@ def _save_attributes_to_file(self):
176
180
"system_prompt" : self .system_prompt ,
177
181
"temperature" : self .temperature ,
178
182
"timeout" : self .timeout ,
183
+ "limit_window" : self .limit_window ,
179
184
}
180
185
config_dir = os .path .join (os .path .expanduser ('~' ), '.config' , 'nvim' )
181
186
os .makedirs (config_dir , exist_ok = True )
@@ -529,9 +534,16 @@ def replace_buffer(match):
529
534
timeout = Timeout (10.0 , read = self .timeout , write = self .timeout ),
530
535
stream = True
531
536
)
537
+ except anthropic .RateLimitError :
538
+ self .nvim .out_write (f"Rate limit reached." )
539
+ except Exception as e :
540
+ self .nvim .err_write (
541
+ f"Error generating response:\n { format_exc ()} \n " )
542
+ return
532
543
533
- self .nvim .current .buffer .append (["" , "<assistant>" , "" ])
534
- response_text = ""
544
+ self .nvim .current .buffer .append (["" , "<assistant>" , "" ])
545
+ response_text = ""
546
+ try :
535
547
for chunk in response_stream :
536
548
if chunk .type == "content_block_delta" :
537
549
response_text += chunk .delta .text
@@ -542,21 +554,29 @@ def replace_buffer(match):
542
554
# Move cursor to the last line of the buffer after appending each chunk
543
555
self .nvim .current .window .cursor = (
544
556
len (self .nvim .current .buffer ), 0 )
545
- self .nvim .current .buffer .append (["" , "</assistant>" , "" , "" ])
557
+ except anthropic .RateLimitError :
558
+ self .nvim .err_write ("Rate limit reached." )
559
+ except Exception as e :
560
+ self .nvim .err_write (
561
+ f"Error generating response:\n { format_exc ()} \n " )
562
+ return
546
563
547
- buffer_number = self .nvim .current .buffer .number
548
- if self .buffer_filenames .get (buffer_number ):
549
- # Make sure filename is sanitized
550
- sanitized_filename = shlex .quote (
551
- self .buffer_filenames [buffer_number ])
552
- else :
553
- # Get a filename if we don't already have one by asking Claude
554
- filename_prompt = """
555
- Based on our conversation, suggest a descriptive filename
556
- for saving this chat. Respond with only the filename,
557
- no explanation. Keep the filename pithy and descriptive.
558
- Extention should be .txt.
559
- """
564
+ self .nvim .current .buffer .append (["" , "</assistant>" , "" , "" ])
565
+
566
+ buffer_number = self .nvim .current .buffer .number
567
+ if self .buffer_filenames .get (buffer_number ):
568
+ # Make sure filename is sanitized
569
+ sanitized_filename = shlex .quote (
570
+ self .buffer_filenames [buffer_number ])
571
+ else :
572
+ # Get a filename if we don't already have one by asking Claude
573
+ filename_prompt = """
574
+ Based on our conversation, suggest a descriptive filename
575
+ for saving this chat. Respond with only the filename,
576
+ no explanation. Keep the filename pithy and descriptive.
577
+ Extention should be .txt.
578
+ """
579
+ try :
560
580
filename_response = completion_method (
561
581
model = self .filename_model ,
562
582
messages = messages + [
@@ -565,24 +585,33 @@ def replace_buffer(match):
565
585
],
566
586
max_tokens = 100
567
587
)
588
+ except anthropic .RateLimitError :
589
+ self .nvim .err_write (
590
+ f"Rate limit reached. Sleeping for { self .limit_window } seconds."
591
+ )
592
+ self .nvim .err_write (
593
+ "You can change the limit window with :ClaudeSettings "
594
+ "limit_window=<seconds>" )
595
+ filename_response = "rate_limit_error_on_filename_response.txt"
596
+ except Exception as e :
597
+ self .nvim .err_write (
598
+ f"Error generating filename:\n { format_exc ()} \n "
599
+ )
600
+ return
568
601
569
- filename = self ._get_filename_from_response (filename_response )
570
- # Escape spaces and special characters in filename
571
- sanitized_filename = shlex .quote (filename )
572
- sanitized_filename = datetime .now ().strftime (
573
- "%Y-%m-%d_%H-%M-%S_" ) + sanitized_filename
574
- self .buffer_filenames [buffer_number ] = sanitized_filename
575
- self .nvim .out_write (f"Saving to { sanitized_filename } \n " )
576
- cmd = f'write! { sanitized_filename } '
577
- self .nvim .command (cmd )
602
+ filename = self ._get_filename_from_response (filename_response )
603
+ # Escape spaces and special characters in filename
604
+ sanitized_filename = shlex .quote (filename )
605
+ sanitized_filename = datetime .now ().strftime (
606
+ "%Y-%m-%d_%H-%M-%S_" ) + sanitized_filename
607
+ self .buffer_filenames [buffer_number ] = sanitized_filename
608
+ self .nvim .out_write (f"Saving to { sanitized_filename } \n " )
609
+ cmd = f'write! { sanitized_filename } '
610
+ self .nvim .command (cmd )
578
611
579
- # Move cursor to the last line of the buffer after appending response
580
- self .nvim .current .window .cursor = (
581
- len (self .nvim .current .buffer ), 0 )
582
-
583
- except Exception as e :
584
- self .nvim .err_write (
585
- f"Error generating response:\n { format_exc ()} \n " )
612
+ # Move cursor to the last line of the buffer after appending response
613
+ self .nvim .current .window .cursor = (
614
+ len (self .nvim .current .buffer ), 0 )
586
615
587
616
def get_claude_models (self ) -> List [str ]:
588
617
"""Get a list of available Anthropic models."""
@@ -916,6 +945,7 @@ def load_claude_settings_command(self, args: List[str]) -> None:
916
945
self .max_context_tokens = DEFAULT_MAX_CONTEXT_TOKENS
917
946
self .temperature = DEFAULT_TEMPERATURE
918
947
self .timeout = DEFAULT_TIMEOUT
948
+ self .limit_window = LIMIT_WINDOW
919
949
self ._save_attributes_to_file ()
920
950
self .nvim .out_write ("Settings restored to defaults.\n " )
921
951
elif args [0 ].lower () == 'save' :
@@ -925,7 +955,7 @@ def load_claude_settings_command(self, args: List[str]) -> None:
925
955
setting , value = [s .strip () for s in args [0 ].split ('=' )]
926
956
if setting not in [
927
957
'model' , 'filename_model' , 'max_tokens' , 'max_context_tokens' ,
928
- 'truncate' , 'temperature' , 'timeout'
958
+ 'truncate' , 'temperature' , 'timeout' , 'limit_window'
929
959
]:
930
960
self .nvim .err_write (
931
961
f"Error: { setting } is not a valid setting.\n " )
@@ -983,12 +1013,19 @@ def load_claude_settings_command(self, args: List[str]) -> None:
983
1013
"Error: timeout must be between 0.0 and 600.0.\n " )
984
1014
return
985
1015
self .timeout = value
1016
+ elif setting == 'limit_window' :
1017
+ value = float (value )
1018
+ if value < 0.0 or value > 600.0 : # 0 to 10 minutes
1019
+ self .nvim .err_write (
1020
+ "Error: limit window must be between 0.0 and 600.0.\n " )
1021
+ return
1022
+ self .limit_window = value
986
1023
else :
987
1024
self .nvim .err_write (
988
1025
"Invalid argument. Use 'defaults' or 'save' or "
989
1026
"'model=...', 'filename_model=...', 'max_tokens=...', "
990
1027
"'max_context_tokens=...', 'truncate=...', 'temperature=...', "
991
- "'timeout=...'\n " )
1028
+ "'timeout=...', 'limit_window=...' \n " )
992
1029
return
993
1030
self .nvim .out_write (
994
1031
f"Current settings:\n "
@@ -999,6 +1036,7 @@ def load_claude_settings_command(self, args: List[str]) -> None:
999
1036
f"truncate_conversation: { self .truncate_conversation } \n "
1000
1037
f"temperature: { self .temperature } \n "
1001
1038
f"timeout: { self .timeout } \n "
1039
+ f"limit_window: { self .limit_window } \n "
1002
1040
)
1003
1041
1004
1042
@pynvim .command ('ClaudeSettings' , nargs = '?' , sync = True )
0 commit comments