1- require 'net/http'
2- require 'uri'
3- require 'json'
41
52module ChatGptService
63 class ChatGptRequest
7- API_URL = 'https://api.openai.com/v1/chat/completions' . freeze
84 MODEL_NAME = 'gpt-4o' . freeze
9- def initialize
10- @api_key = Rails . application . credentials . openai [ :api_key ]
11- unless @api_key
12- raise "OpenAI API key is missing. Please set it in environment variables or Rails credentials."
13- end
14- end
15-
16- def get_response ( options = { } )
17- prompt = construct_prompt ( options )
18- response = make_chat_gpt_request ( prompt , options [ :response_format_needed ] )
19- if options [ :response_format_needed ]
20- format_response ( response )
21- else
22- response
23- end
24- end
25-
26- private
27-
28- def construct_prompt ( options )
29- if options [ :request_for_comment ] && options [ :file ]
30- construct_prompt_for_rfc ( options [ :request_for_comment ] , options [ :file ] )
31- else
32- format_prompt ( options )
33- end
34- end
35-
36- def construct_prompt_for_rfc ( request_for_comment , file )
37- submission = request_for_comment . submission
38- test_run_results = Testrun . where ( submission_id : submission . id ) . map ( &:log ) . join ( "\n " )
39- options = {
40- learner_solution : file . content ,
41- exercise : submission . exercise . description ,
42- test_results : test_run_results ,
43- question : request_for_comment . question
44- }
45- format_prompt ( options )
46- end
475
48- def format_prompt ( options )
49- if I18n . locale == :en
50- file_path = Rails . root . join ( 'app' , 'services/chat_gpt_service/chat_gpt_prompts' , 'prompt_en.xml' )
51- prompt = File . read ( file_path )
52- prompt . gsub! ( "[Learner's Code]" , options [ :learner_solution ] || "" )
53- prompt . gsub! ( "[Task]" , options [ :exercise ] || "" )
54- prompt . gsub! ( "[Error Message]" , options [ :test_results ] || "" )
55- prompt . gsub! ( "[Student Question]" , options [ :question ] || "" )
56- else
57- file_path = Rails . root . join ( 'app' , 'services/chat_gpt_service/chat_gpt_prompts' , 'prompt_de.xml' )
58- prompt = File . read ( file_path )
59- prompt . gsub! ( "[Code des Lernenden]" , options [ :learner_solution ] || "" )
60- prompt . gsub! ( "[Aufgabenstellung]" , options [ :exercise ] || "" )
61- prompt . gsub! ( "[Fehlermeldung]" , options [ :test_results ] || "" )
62- prompt . gsub! ( "[Frage des Studierenden]" , options [ :question ] || "" )
63- end
64-
65- prompt
6+ def initialize
7+ raise Gpt ::Error ::InvalidApiKey if fetch_api_key . blank?
8+ @client = OpenAI ::Client . new ( access_token : fetch_api_key )
669 end
6710
6811 def make_chat_gpt_request ( prompt , response_format_needed = false )
69- url = URI . parse ( API_URL )
70- data = {
71- model : MODEL_NAME ,
72- messages : [ { role : 'user' , content : prompt } ] ,
73- max_tokens : 2048
74- }
75- if response_format_needed
76- response_format = JSON . parse ( File . read ( Rails . root . join ( 'app' , 'services/chat_gpt_service/chat_gpt_prompts' , 'response_format.json' ) ) )
77- data [ :response_format ] = response_format
78- end
79-
80- http = Net ::HTTP . new ( url . host , url . port )
81- http . use_ssl = true
12+ wrap_api_error! do
13+ data = {
14+ model : MODEL_NAME ,
15+ messages : [ { role : 'user' , content : prompt } ] ,
16+ temperature : 0.7 # Lower values insure reproducibility
17+ }
8218
83- request = Net ::HTTP ::Post . new ( url . path , {
84- 'Content-Type' => 'application/json' ,
85- 'Authorization' => "Bearer #{ @api_key } "
86- } )
87- request . body = data . to_json
19+ if response_format_needed
20+ response_format = JSON . parse ( File . read ( Rails . root . join ( 'app' , 'services/chat_gpt_service/chat_gpt_prompts' , 'response_format.json' ) ) )
21+ data [ :response_format ] = response_format
22+ end
8823
89- begin
90- response = http . request ( request )
91- json_response = JSON . parse ( response . body )
24+ begin
25+ response = @client . chat ( parameters : data )
26+ json_response = response . dig ( 'choices' , 0 , 'message' , 'content' )
9227
93- if response . is_a? ( Net ::HTTPSuccess )
94- json_response . dig ( 'choices' , 0 , 'message' , 'content' )
95- else
96- error_message = json_response . dig ( 'error' , 'message' ) || 'Unknown error'
97- Rails . logger . error "ChatGPT API Error: #{ error_message } "
98- raise "ChatGPT API Error: #{ error_message } "
28+ if json_response
29+ json_response
30+ else
31+ error_message = response . dig ( 'error' , 'message' ) || 'Unknown error'
32+ Rails . logger . error "ChatGPT API Error: #{ error_message } "
33+ raise "ChatGPT API Error: #{ error_message } "
34+ end
35+ rescue JSON ::ParserError => e
36+ Rails . logger . error "Failed to parse ChatGPT response: #{ e . message } "
37+ raise "Failed to parse ChatGPT response: #{ e . message } "
38+ rescue StandardError => e
39+ Rails . logger . error "Error while making request to ChatGPT: #{ e . message } "
40+ raise e
9941 end
100- rescue JSON ::ParserError => e
101- Rails . logger . error "Failed to parse ChatGPT response: #{ e . message } "
102- raise "Failed to parse ChatGPT response: #{ e . message } "
103- rescue StandardError => e
104- Rails . logger . error "Error while making request to ChatGPT: #{ e . message } "
105- raise e
10642 end
10743 end
10844
109- def format_response ( response )
110- parsed_response = JSON . parse ( response )
111- requirements_comments = ''
112- if parsed_response [ 'requirements' ]
113- requirements_comments = parsed_response [ 'requirements' ] . map { |req | req [ 'comment' ] } . join ( "\n " )
114- end
45+ private
11546
116- line_specific_comments = [ ]
117- if parsed_response [ 'line_specific_comments' ]
118- line_specific_comments = parsed_response [ 'line_specific_comments' ] . map do |line_comment |
119- {
120- line_number : line_comment [ 'line_number' ] ,
121- comment : line_comment [ 'comment' ]
122- }
123- end
124- end
47+ def fetch_api_key
48+ api_key = Rails . application . credentials . openai [ :api_key ]
49+ raise "OpenAI API key is missing. Please set it in environment variables or Rails credentials." unless api_key
50+ api_key
51+ end
12552
126- {
127- requirements_comments : requirements_comments ,
128- line_specific_comments : line_specific_comments
129- }
53+ def wrap_api_error!
54+ yield
55+ rescue Faraday ::UnauthorizedError , OpenAI ::Error => e
56+ raise Gpt ::Error ::InvalidApiKey . new ( "Could not authenticate with OpenAI: #{ e . message } " )
57+ rescue Faraday ::Error => e
58+ raise Gpt ::Error ::InternalServerError . new ( "Could not communicate with OpenAI: #{ e . inspect } " )
59+ rescue Net ::OpenTimeout , Net ::ReadTimeout , Errno ::ECONNRESET , SocketError , EOFError => e
60+ raise Gpt ::Error . new ( e )
13061 end
62+
13163 end
132- end
64+ end
0 commit comments