@@ -73,22 +73,56 @@ pub async fn generate(patch: String, remaining_tokens: usize, model: Model, sett
7373 . and_then ( |s| s. max_commit_length )
7474 . or ( config:: APP . max_commit_length ) ;
7575
76+ // Check if we have a valid API key configuration
77+ let has_valid_api_key = if let Some ( custom_settings) = settings {
78+ custom_settings
79+ . openai_api_key
80+ . as_ref ( )
81+ . map ( |key| !key. is_empty ( ) && key != "<PLACE HOLDER FOR YOUR API KEY>" )
82+ . unwrap_or ( false )
83+ } else {
84+ // Check environment variable or config
85+ config:: APP
86+ . openai_api_key
87+ . as_ref ( )
88+ . map ( |key| !key. is_empty ( ) && key != "<PLACE HOLDER FOR YOUR API KEY>" )
89+ . unwrap_or ( false )
90+ || std:: env:: var ( "OPENAI_API_KEY" )
91+ . map ( |key| !key. is_empty ( ) )
92+ . unwrap_or ( false )
93+ } ;
94+
95+ if !has_valid_api_key {
96+ bail ! ( "OpenAI API key not configured. Please set your API key using:\n git-ai config set openai-api-key <your-key>\n or set the OPENAI_API_KEY environment variable." ) ;
97+ }
98+
7699 // Use custom settings if provided
77100 if let Some ( custom_settings) = settings {
78101 if let Some ( api_key) = & custom_settings. openai_api_key {
79102 if !api_key. is_empty ( ) && api_key != "<PLACE HOLDER FOR YOUR API KEY>" {
80- let config = openai:: create_openai_config ( custom_settings) ?;
81- let client = Client :: with_config ( config) ;
82- let model_str = model. to_string ( ) ;
103+ match openai:: create_openai_config ( custom_settings) {
104+ Ok ( config) => {
105+ let client = Client :: with_config ( config) ;
106+ let model_str = model. to_string ( ) ;
83107
84- match generate_commit_message_multi_step ( & client, & model_str, & patch, max_length) . await {
85- Ok ( message) => return Ok ( openai:: Response { response : message } ) ,
86- Err ( e) => {
87- log:: warn!( "Multi-step generation with custom settings failed: {e}" ) ;
88- if let Some ( session) = debug_output:: debug_session ( ) {
89- session. set_multi_step_error ( e. to_string ( ) ) ;
108+ match generate_commit_message_multi_step ( & client, & model_str, & patch, max_length) . await {
109+ Ok ( message) => return Ok ( openai:: Response { response : message } ) ,
110+ Err ( e) => {
111+ // Check if it's an API key error
112+ if e. to_string ( ) . contains ( "invalid_api_key" ) || e. to_string ( ) . contains ( "Incorrect API key" ) {
113+ bail ! ( "Invalid OpenAI API key. Please check your API key configuration." ) ;
114+ }
115+ log:: warn!( "Multi-step generation with custom settings failed: {e}" ) ;
116+ if let Some ( session) = debug_output:: debug_session ( ) {
117+ session. set_multi_step_error ( e. to_string ( ) ) ;
118+ }
119+ }
90120 }
91121 }
122+ Err ( e) => {
123+ // If config creation fails due to API key, propagate the error
124+ return Err ( e) ;
125+ }
92126 }
93127 }
94128 }
@@ -102,6 +136,10 @@ pub async fn generate(patch: String, remaining_tokens: usize, model: Model, sett
102136 match generate_commit_message_multi_step ( & client, & model_str, & patch, max_length) . await {
103137 Ok ( message) => return Ok ( openai:: Response { response : message } ) ,
104138 Err ( e) => {
139+ // Check if it's an API key error
140+ if e. to_string ( ) . contains ( "invalid_api_key" ) || e. to_string ( ) . contains ( "Incorrect API key" ) {
141+ bail ! ( "Invalid OpenAI API key. Please check your API key configuration." ) ;
142+ }
105143 log:: warn!( "Multi-step generation failed: {e}" ) ;
106144 if let Some ( session) = debug_output:: debug_session ( ) {
107145 session. set_multi_step_error ( e. to_string ( ) ) ;
@@ -132,8 +170,10 @@ pub async fn generate(patch: String, remaining_tokens: usize, model: Model, sett
132170 match settings {
133171 Some ( custom_settings) => {
134172 // Create a client with custom settings
135- let config = openai:: create_openai_config ( custom_settings) ?;
136- openai:: call_with_config ( request, config) . await
173+ match openai:: create_openai_config ( custom_settings) {
174+ Ok ( config) => openai:: call_with_config ( request, config) . await ,
175+ Err ( e) => Err ( e)
176+ }
137177 }
138178 None => {
139179 // Use the default global config
@@ -158,3 +198,75 @@ pub fn get_instruction_token_count(model: &Model) -> Result<usize> {
158198 let template = get_instruction_template ( ) ?;
159199 model. count_tokens ( & template)
160200}
201+
202+ #[ cfg( test) ]
203+ mod tests {
204+ use super :: * ;
205+
206+ #[ tokio:: test]
207+ async fn test_missing_api_key_error ( ) {
208+ // Create settings with no API key
209+ let settings = Settings {
210+ openai_api_key : None ,
211+ model : Some ( "gpt-4o-mini" . to_string ( ) ) ,
212+ max_tokens : Some ( 1024 ) ,
213+ max_commit_length : Some ( 72 ) ,
214+ timeout : Some ( 30 )
215+ } ;
216+
217+ // Temporarily clear the environment variable
218+ let original_key = std:: env:: var ( "OPENAI_API_KEY" ) . ok ( ) ;
219+ std:: env:: remove_var ( "OPENAI_API_KEY" ) ;
220+
221+ // Test that generate returns an error for missing API key
222+ let result = generate (
223+ "diff --git a/test.txt b/test.txt\n +Hello World" . to_string ( ) ,
224+ 1024 ,
225+ Model :: GPT4oMini ,
226+ Some ( & settings)
227+ )
228+ . await ;
229+
230+ // Restore original environment variable if it existed
231+ if let Some ( key) = original_key {
232+ std:: env:: set_var ( "OPENAI_API_KEY" , key) ;
233+ }
234+
235+ assert ! ( result. is_err( ) ) ;
236+ let error_message = result. unwrap_err ( ) . to_string ( ) ;
237+ assert ! (
238+ error_message. contains( "OpenAI API key not configured" ) ,
239+ "Expected error message about missing API key, got: {}" ,
240+ error_message
241+ ) ;
242+ }
243+
244+ #[ tokio:: test]
245+ async fn test_invalid_api_key_error ( ) {
246+ // Create settings with invalid API key
247+ let settings = Settings {
248+ openai_api_key : Some ( "<PLACE HOLDER FOR YOUR API KEY>" . to_string ( ) ) ,
249+ model : Some ( "gpt-4o-mini" . to_string ( ) ) ,
250+ max_tokens : Some ( 1024 ) ,
251+ max_commit_length : Some ( 72 ) ,
252+ timeout : Some ( 30 )
253+ } ;
254+
255+ // Test that generate returns an error for invalid API key
256+ let result = generate (
257+ "diff --git a/test.txt b/test.txt\n +Hello World" . to_string ( ) ,
258+ 1024 ,
259+ Model :: GPT4oMini ,
260+ Some ( & settings)
261+ )
262+ . await ;
263+
264+ assert ! ( result. is_err( ) ) ;
265+ let error_message = result. unwrap_err ( ) . to_string ( ) ;
266+ assert ! (
267+ error_message. contains( "OpenAI API key not configured" ) ,
268+ "Expected error message about invalid API key, got: {}" ,
269+ error_message
270+ ) ;
271+ }
272+ }
0 commit comments