88 logger ,
99 read_config ,
1010)
11- from aicodebot .prompts import PERSONALITIES , generate_files_context , get_prompt
11+ from aicodebot .prompts import DEFAULT_PERSONALITY , PERSONALITIES , generate_files_context , get_prompt
1212from langchain .callbacks .base import BaseCallbackHandler
1313from langchain .chains import LLMChain
1414from langchain .chat_models import ChatOpenAI
@@ -73,7 +73,7 @@ def alignment(response_token_size, verbose):
7373 llm = ChatOpenAI (
7474 model = model ,
7575 temperature = CREATIVE_TEMPERATURE ,
76- openai_api_key = config ["OPENAI_API_KEY " ],
76+ openai_api_key = config ["openai_api_key " ],
7777 max_tokens = response_token_size ,
7878 verbose = verbose ,
7979 streaming = True ,
@@ -136,7 +136,7 @@ def commit(verbose, response_token_size, yes, skip_pre_commit):
136136 # Set up the language model
137137 llm = ChatOpenAI (
138138 model = model ,
139- openai_api_key = config ["OPENAI_API_KEY " ],
139+ openai_api_key = config ["openai_api_key " ],
140140 temperature = PRECISE_TEMPERATURE ,
141141 max_tokens = DEFAULT_MAX_TOKENS ,
142142 verbose = verbose ,
@@ -168,6 +168,99 @@ def commit(verbose, response_token_size, yes, skip_pre_commit):
168168 Path .unlink (temp_file_name )
169169
170170
171+ @cli .command ()
172+ @click .option ("-v" , "--verbose" , count = True )
173+ @click .option ("--openai-api-key" , envvar = "OPENAI_API_KEY" , help = "Your OpenAI API key" )
174+ def configure (verbose , openai_api_key ):
175+ """Create or update the config file"""
176+
177+ # --------------- Check for an existing key or set up defaults --------------- #
178+
179+ config_data_defaults = {
180+ "version" : 1.1 ,
181+ "openai_api_key" : openai_api_key ,
182+ "personality" : DEFAULT_PERSONALITY .name ,
183+ }
184+
185+ config_data = config_data_defaults .copy ()
186+ config_file = get_config_file ()
187+
188+ existing_config = read_config ()
189+ if existing_config :
190+ console .print (f"Config file already exists at { get_config_file ()} ." )
191+ click .confirm ("Do you want to rerun configure and overwrite it?" , default = False , abort = True )
192+ config_data .update (
193+ {"openai_api_key" : existing_config ["openai_api_key" ], "personality" : existing_config ["personality" ]}
194+ )
195+
196+ config_data = config_data_defaults .copy ()
197+
198+ def write_config_file (config_data ):
199+ with Path .open (config_file , "w" ) as f :
200+ yaml .dump (config_data , f )
201+ console .print (f"✅ Created config file at { config_file } " )
202+
203+ is_terminal = sys .stdout .isatty ()
204+ if not is_terminal :
205+ if config_data ["openai_api_key" ] is None :
206+ raise click .ClickException (
207+ "🛑 No OpenAI API key found.\n "
208+ "Please set the OPENAI_API_KEY environment variable or call configure with --openai-api-key set."
209+ )
210+ else :
211+ # If we are not in a terminal, then we can't ask for input, so just use the defaults and write the file
212+ write_config_file (config_data )
213+ return
214+
215+ # ---------------- Collect the OPENAI_API_KEY and validate it ---------------- #
216+
217+ if config_data ["openai_api_key" ] is None :
218+ console .print (
219+ "An OpenAI API key is required to use AICodeBot. You can get one for free on the OpenAI website.\n "
220+ )
221+ openai_api_key_url = "https://platform.openai.com/account/api-keys"
222+ if click .confirm ("Open the OpenAI API keys page for you in a browser?" , default = False ):
223+ webbrowser .open (openai_api_key_url )
224+
225+ config_data ["openai_api_key" ] = click .prompt (
226+ "Please enter your OpenAI API key" , default = config_data ["openai_api_key" ]
227+ )
228+
229+ # Validate the API key
230+ try :
231+ openai .api_key = config_data ["openai_api_key" ]
232+ click .echo ("Validating the OpenAI API key" )
233+ engine .Engine .list ()
234+ except Exception as e :
235+ raise click .ClickException (f"Failed to validate the API key: { str (e )} " ) from e
236+ click .echo ("✅ The API key is valid." )
237+
238+ # ---------------------- Collect the personality choice ---------------------- #
239+
240+ # Pull the choices from the name from each of the PERSONALITIES
241+ personality_choices = "\n How would you like your AI to act? You can choose from the following personalities:\n "
242+ for key , personality in PERSONALITIES .items ():
243+ personality_choices += f"\t { key } - { personality .description } \n "
244+ console .print (personality_choices )
245+
246+ config_data ["personality" ] = click .prompt (
247+ "Please choose a personality" ,
248+ type = click .Choice (PERSONALITIES .keys (), case_sensitive = False ),
249+ default = DEFAULT_PERSONALITY .name ,
250+ )
251+
252+ write_config_file (config_data )
253+ console .print ("✅ Configuration complete, you're ready to run aicodebot!\n " )
254+
255+ # After writing the config file, print the usage for the top-level group
256+ ctx = click .get_current_context ()
257+ while ctx .parent is not None :
258+ ctx = ctx .parent
259+ console .print (ctx .get_help ())
260+
261+ console .print ("\n Don't know where to start? Try running `aicodebot alignment`." )
262+
263+
171264@cli .command (context_settings = {"ignore_unknown_options" : True })
172265@click .argument ("command" , nargs = - 1 )
173266@click .option ("-v" , "--verbose" , count = True )
@@ -208,7 +301,7 @@ def debug(command, verbose):
208301 llm = ChatOpenAI (
209302 model = model ,
210303 temperature = PRECISE_TEMPERATURE ,
211- openai_api_key = config ["OPENAI_API_KEY " ],
304+ openai_api_key = config ["openai_api_key " ],
212305 max_tokens = DEFAULT_MAX_TOKENS ,
213306 verbose = verbose ,
214307 streaming = True ,
@@ -241,7 +334,7 @@ def fun_fact(verbose, response_token_size):
241334 model = model ,
242335 temperature = PRECISE_TEMPERATURE ,
243336 max_tokens = response_token_size ,
244- openai_api_key = config ["OPENAI_API_KEY " ],
337+ openai_api_key = config ["openai_api_key " ],
245338 verbose = verbose ,
246339 streaming = True ,
247340 callbacks = [RichLiveCallbackHandler (live )],
@@ -271,7 +364,7 @@ def review(commit, verbose):
271364 logger .trace (f"Prompt: { prompt } " )
272365
273366 # Check the size of the diff context and adjust accordingly
274- response_token_size = DEFAULT_MAX_TOKENS
367+ response_token_size = DEFAULT_MAX_TOKENS * 2
275368 request_token_size = get_token_length (diff_context ) + get_token_length (prompt .template )
276369 model = get_llm_model (request_token_size )
277370 if model is None :
@@ -281,7 +374,7 @@ def review(commit, verbose):
281374 llm = ChatOpenAI (
282375 model = model ,
283376 temperature = PRECISE_TEMPERATURE ,
284- openai_api_key = config ["OPENAI_API_KEY " ],
377+ openai_api_key = config ["openai_api_key " ],
285378 max_tokens = response_token_size ,
286379 verbose = verbose ,
287380 streaming = True ,
@@ -294,28 +387,6 @@ def review(commit, verbose):
294387 chain .run (diff_context )
295388
296389
297- @cli .command ()
298- @click .option ("--openai-api-key" , "-k" , help = "Your OpenAI API key" )
299- @click .option ("--gpt-4-supported" , "-4" , help = "Whether you have access to GPT-4" , is_flag = True )
300- def setup (openai_api_key , gpt_4_supported ):
301- """Set up the configuration file with your OpenAI API key
302- If the config file already exists, it will ask you if you want to remove it and recreate it.
303- """
304- config_file = get_config_file ()
305- if config_file .exists ():
306- if not click .confirm (
307- f"The config file already exists at { config_file } . Do you want to remove it and recreate it?"
308- ):
309- console .print ("Setup cancelled. 🚫" )
310- sys .exit (1 )
311-
312- # Remove the existing config file
313- config_file .unlink ()
314-
315- # Call the setup_config function with the provided arguments
316- setup_config (openai_api_key , gpt_4_supported )
317-
318-
319390@cli .command
320391@click .option ("--request" , "-r" , help = "What to ask your sidekick to do" )
321392@click .option ("-v" , "--verbose" , count = True )
@@ -349,7 +420,7 @@ def sidekick(request, verbose, response_token_size, files):
349420
350421 llm = ChatOpenAI (
351422 model = model ,
352- openai_api_key = config ["OPENAI_API_KEY " ],
423+ openai_api_key = config ["openai_api_key " ],
353424 temperature = PRECISE_TEMPERATURE ,
354425 max_tokens = response_token_size ,
355426 verbose = verbose ,
@@ -398,73 +469,14 @@ def sidekick(request, verbose, response_token_size, files):
398469# ---------------------------------------------------------------------------- #
399470
400471
401- def setup_config (openai_api_key = None , gpt_4_supported = None ):
402- config = read_config ()
403- openai .api_key = openai_api_key
404- if config :
405- openai .api_key = config ["OPENAI_API_KEY" ]
406- logger .success (f"Using OpenAI API key from { get_config_file ()} " )
407- return config
408- elif os .getenv ("OPENAI_API_KEY" ):
409- logger .info ("Using OPENAI_API_KEY environment variable" )
410- openai .api_key = os .getenv ("OPENAI_API_KEY" )
411-
412- config_file = get_config_file ()
413- console .print (f"[bold red]The config file does not exist.[/bold red]\n Let's set that up for you at { config_file } \n " )
414-
415- if not openai .api_key :
416- openai_api_key_url = "https://platform.openai.com/account/api-keys"
417-
418- console .print (
419- "First, an OpenAI API key is required to use AICodeBot. You can get one for free on the OpenAI website.\n "
420- )
421-
422- if click .confirm ("Open the OpenAI API keys page for you in a browser?" ):
423- webbrowser .open (openai_api_key_url )
424-
425- openai .api_key = click .prompt ("Please enter your OpenAI API key" )
426-
427- # Validate the API key and check if it supports GPT-4
428- if gpt_4_supported is None :
429- try :
430- click .echo ("Validating the API key, and checking if GPT-4 is supported..." )
431- engines = engine .Engine .list ()
432- logger .trace (f"Engines: { engines } " )
433- gpt_4_supported = "gpt-4" in [engine .id for engine in engines .data ]
434- if gpt_4_supported :
435- click .echo ("✅ The API key is valid and supports GPT-4." )
436- else :
437- click .echo ("✅ The API key is valid, but does not support GPT-4. GPT-3.5 will be used instead." )
438- except Exception as e :
439- raise click .ClickException (f"Failed to validate the API key: { str (e )} " ) from e
440-
441- # Pull the choices from the name from each of the PERSONALITIES
442- personality_choices = "\n How would you like your AI to act? You can choose from the following personalities:\n "
443- for key , personality in PERSONALITIES .items ():
444- personality_choices += f"\t { key } - { personality .description } \n "
445- console .print (personality_choices )
446-
447- personality = click .prompt (
448- "Please choose a personality" ,
449- type = click .Choice (PERSONALITIES .keys (), case_sensitive = False ),
450- default = list (PERSONALITIES .keys ())[0 ],
451- )
452-
453- config_data = {
454- "config_version" : 1 ,
455- "OPENAI_API_KEY" : openai .api_key ,
456- "gpt_4_supported" : gpt_4_supported ,
457- "personality" : personality ,
458- }
459-
460- with Path .open (config_file , "w" ) as f :
461- yaml .dump (config_data , f )
462-
463- console .print (
464- f"[bold green]Created { config_file } with your OpenAI API key.[/bold green] "
465- "Now, please re-run aicodebot and let's get started!"
466- )
467- sys .exit (0 )
472+ def setup_config ():
473+ existing_config = read_config ()
474+ if not existing_config :
475+ console .print ("No config file found. Running configure...\n " )
476+ configure .callback (openai_api_key = None , verbose = 0 )
477+ sys .exit ()
478+ else :
479+ return existing_config
468480
469481
470482class RichLiveCallbackHandler (BaseCallbackHandler ):
0 commit comments