@@ -53,22 +53,30 @@ defmodule CadetWeb.AICodeAnalysisController do
5353 defp check_llm_grading_parameters ( llm_api_key , llm_model , llm_api_url , llm_course_level_prompt ) do
5454 cond do
5555 is_nil ( llm_api_key ) ->
56- { :error , "LLM API key is not configured for this course or in the environment" }
56+ { :parameter_error , "LLM API key is not configured for this course or in the environment" }
5757
5858 is_nil ( llm_model ) or llm_model == "" ->
59- { :error , "LLM model is not configured for this course" }
59+ { :parameter_error , "LLM model is not configured for this course" }
6060
6161 is_nil ( llm_api_url ) or llm_api_url == "" ->
62- { :error , "LLM API URL is not configured for this course" }
62+ { :parameter_error , "LLM API URL is not configured for this course" }
6363
6464 is_nil ( llm_course_level_prompt ) or llm_course_level_prompt == "" ->
65- { :error , "LLM course-level prompt is not configured for this course" }
65+ { :parameter_error , "LLM course-level prompt is not configured for this course" }
6666
6767 true ->
6868 { :ok }
6969 end
7070 end
7171
72+ defp ensure_llm_enabled ( course ) do
73+ if course . enable_llm_grading do
74+ { :ok }
75+ else
76+ { :error , { :forbidden , "LLM grading is not enabled for this course" } }
77+ end
78+ end
79+
7280 @ doc """
7381 Fetches the question details and answers based on submissionid and questionid and generates AI-generated comments.
7482 """
@@ -78,65 +86,57 @@ defmodule CadetWeb.AICodeAnalysisController do
7886 "course_id" => course_id
7987 } )
8088 when is_ecto_id ( submission_id ) do
81- # Check if LLM grading is enabled for this course
82- question_id = String . to_integer ( question_id )
83-
84- case Courses . get_course_config ( course_id ) do
85- { :ok , course } ->
86- if course . enable_llm_grading do
87- # Get API key from course config or fall back to environment variable
88- decrypted_api_key = decrypt_llm_api_key ( course . llm_api_key )
89- api_key = decrypted_api_key || Application . get_env ( :openai , :api_key )
90-
91- case check_llm_grading_parameters (
92- api_key ,
93- course . llm_model ,
94- course . llm_api_url ,
95- course . llm_course_level_prompt
96- ) do
97- { :error , error_msg } ->
98- conn
99- |> put_status ( :bad_request )
100- |> text ( error_msg )
101-
102- { :ok } ->
103- case Assessments . get_answers_in_submission ( submission_id , question_id ) do
104- { :ok , { answers , _assessment } } ->
105- case answers do
106- [ ] ->
107- conn
108- |> put_status ( :not_found )
109- |> text ( "No answer found for the given submission and question_id" )
110-
111- _ ->
112- # Get head of answers (should only be one answer for given submission
113- # and question since we filter to only 1 question)
114- analyze_code (
115- conn ,
116- % {
117- answer: hd ( answers ) ,
118- submission_id: submission_id ,
119- question_id: question_id ,
120- api_key: api_key ,
121- llm_model: course . llm_model ,
122- llm_api_url: course . llm_api_url ,
123- course_prompt: course . llm_course_level_prompt ,
124- assessment_prompt: Assessments . get_llm_assessment_prompt ( question_id )
125- }
126- )
127- end
128-
129- { :error , { status , message } } ->
130- conn
131- |> put_status ( status )
132- |> text ( message )
133- end
134- end
135- else
89+ with { qid , "" } <- Integer . parse ( question_id ) ,
90+ { :ok , course } <- Courses . get_course_config ( course_id ) ,
91+ { :ok } <- ensure_llm_enabled ( course ) ,
92+ { :ok , key } <- decrypt_llm_api_key ( course . llm_api_key ) ,
93+ { :ok } <-
94+ check_llm_grading_parameters (
95+ key ,
96+ course . llm_model ,
97+ course . llm_api_url ,
98+ course . llm_course_level_prompt
99+ ) ,
100+ { :ok , { answers , _ } } <- Assessments . get_answers_in_submission ( submission_id , qid ) do
101+ # Get head of answers (should only be one answer for given submission
102+ # and question since we filter to only 1 question)
103+ case answers do
104+ [ ] ->
136105 conn
137- |> put_status ( :forbidden )
138- |> text ( "LLM grading is not enabled for this course" )
139- end
106+ |> put_status ( :not_found )
107+ |> text ( "No answer found for the given submission and question_id" )
108+
109+ _ ->
110+ analyze_code (
111+ conn ,
112+ % {
113+ answer: hd ( answers ) ,
114+ submission_id: submission_id ,
115+ question_id: qid ,
116+ api_key: key ,
117+ llm_model: course . llm_model ,
118+ llm_api_url: course . llm_api_url ,
119+ course_prompt: course . llm_course_level_prompt ,
120+ assessment_prompt: Assessments . get_llm_assessment_prompt ( qid )
121+ }
122+ )
123+ end
124+ else
125+ :error ->
126+ conn
127+ |> put_status ( :bad_request )
128+ |> text ( "Invalid question ID format" )
129+
130+ { :decrypt_error , err } ->
131+ conn
132+ |> put_status ( :internal_server_error )
133+ |> text ( "Failed to decrypt LLM API key: #{ inspect ( err ) } " )
134+
135+ # Errors for check_llm_grading_parameters
136+ { :parameter_error , error_msg } ->
137+ conn
138+ |> put_status ( :bad_request )
139+ |> text ( error_msg )
140140
141141 { :error , { status , message } } ->
142142 conn
@@ -260,6 +260,20 @@ defmodule CadetWeb.AICodeAnalysisController do
260260
261261 json ( conn , % { "comments" => filtered_comments } )
262262
263+ { :ok , other } ->
264+ save_comment (
265+ submission_id ,
266+ question_id ,
267+ system_prompt ,
268+ formatted_answer ,
269+ Jason . encode! ( other ) ,
270+ "Unexpected JSON shape"
271+ )
272+
273+ conn
274+ |> put_status ( :bad_gateway )
275+ |> text ( "Unexpected response format from OpenAI API" )
276+
263277 { :error , err } ->
264278 save_comment (
265279 submission_id ,
@@ -393,18 +407,21 @@ defmodule CadetWeb.AICodeAnalysisController do
393407 ciphertext = binary_part ( decoded , 32 , byte_size ( decoded ) - 32 )
394408
395409 case :crypto . crypto_one_time_aead ( :aes_gcm , key , iv , ciphertext , "" , tag , false ) do
396- plain_text when is_binary ( plain_text ) -> plain_text
397- _ -> nil
410+ plain_text when is_binary ( plain_text ) -> { :ok , plain_text }
411+ _ -> { :decrypt_error , :decryption_failed }
398412 end
399413
400414 _ ->
401- Logger . error ( "Failed to decode encrypted key" )
402- nil
415+ Logger . error (
416+ "Failed to decode encrypted key, is it a valid AES-256 key of 16, 24 or 32 bytes?"
417+ )
418+
419+ { :decrypt_error , :decryption_failed }
403420 end
404421
405422 _ ->
406- Logger . error ( "Encryption key not configured properly " )
407- nil
423+ Logger . error ( "Encryption key not configured" )
424+ { :decrypt_error , :invalid_encryption_key }
408425 end
409426 end
410427end
0 commit comments