Skip to content

Commit a9e2b2a

Browse files
committed
Work on sentry comments
1 parent f794a14 commit a9e2b2a

File tree

5 files changed

+88
-75
lines changed

5 files changed

+88
-75
lines changed

config/dev.secrets.exs.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ config :cadet,
9999
]
100100

101101
config :openai,
102-
# TODO: Input your own AES-256 encryption key here for encrypting LLM API keys
102+
# TODO: Input your own AES-256 encryption key here for encrypting LLM API keys of 16, 24 or 32 bytes
103103
encryption_key: "<ADD YOUR OWN KEY HERE>"
104104

105105
# config :sentry,

lib/cadet/courses/course.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ defmodule Cadet.Courses.Course do
9292
# Store both the IV, ciphertext and tag
9393
encrypted = Base.encode64(iv <> tag <> ciphertext)
9494
else
95-
nil
95+
{:error, :invalid_encryption_key}
9696
end
9797
end
9898

@@ -102,7 +102,7 @@ defmodule Cadet.Courses.Course do
102102
encrypted = encrypt_llm_api_key(llm_api_key)
103103

104104
case encrypted do
105-
nil ->
105+
{:error, :invalid_encryption_key} ->
106106
add_error(
107107
changeset,
108108
:llm_api_key,

lib/cadet_web/controllers/generate_ai_comments.ex

Lines changed: 85 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -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
410427
end

priv/repo/migrations/20240320000001_add_llm_api_key_to_courses.exs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ defmodule Cadet.Repo.Migrations.AddLlmApiKeyToCourses do
33

44
def up do
55
alter table(:courses) do
6-
add(:course_level_prompt, :text, null: true)
76
add(:llm_api_key, :text, null: true)
87
add(:llm_model, :text, null: true)
98
add(:llm_api_url, :text, null: true)
@@ -13,7 +12,6 @@ defmodule Cadet.Repo.Migrations.AddLlmApiKeyToCourses do
1312

1413
def down do
1514
alter table(:courses) do
16-
remove(:course_level_prompt)
1715
remove(:llm_course_level_prompt)
1816
remove(:llm_api_key)
1917
remove(:llm_model)

test/cadet_web/controllers/ai_code_analysis_controller_test.exs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,6 @@ defmodule CadetWeb.AICodeAnalysisControllerTest do
142142
question: question,
143143
answer: answer
144144
} do
145-
random_submission_id = 123
146-
147145
# Make the API call that should fail
148146
with_mock HTTPoison, [:passthrough],
149147
post: fn _url, _body, _headers, _opts ->

0 commit comments

Comments
 (0)