Module: IRB::AI
- Defined in:
- lib/irb/ai.rb,
lib/irb/ai/version.rb
Constant Summary collapse
- NULL_VALUE =
"NULL RETURN VALUE"
- VERSION =
"0.1.0"
Class Method Summary collapse
- .ai_client ⇒ Object
- .code_around_binding(b) ⇒ Object
- .debug_mode? ⇒ Boolean
- .execute_expression(expression:, context_binding:, context_obj:) ⇒ Object
- .generate_message(expression:, context_binding:, context_obj:, traces:, exception:, return_value:) ⇒ Object
- .information_section(expression:, context_binding:, context_obj:, traces:, exception:, return_value:) ⇒ Object
- .model ⇒ Object
- .model=(model) ⇒ Object
- .register_irb_commands ⇒ Object
- .request_section(expression:, exception:) ⇒ Object
- .send_messages(expression:, context_binding:, context_obj:, traces:, exception:, return_value:) ⇒ Object
Class Method Details
.ai_client ⇒ Object
18 19 20 |
# File 'lib/irb/ai.rb', line 18 def ai_client @ai_client ||= OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"]) end |
.code_around_binding(b) ⇒ Object
239 240 241 242 243 244 245 246 247 |
# File 'lib/irb/ai.rb', line 239 def code_around_binding(b) original_colorize = IRB.conf[:USE_COLORIZE] IRB.conf[:USE_COLORIZE] = false file, line = b.source_location File.read(file).lines[(line - 20)..(line + 4)].join ensure IRB.conf[:USE_COLORIZE] = original_colorize end |
.debug_mode? ⇒ Boolean
14 15 16 |
# File 'lib/irb/ai.rb', line 14 def debug_mode? ENV["IRB_AI_DEBUG"] && !ENV["IRB_AI_DEBUG"].empty? end |
.execute_expression(expression:, context_binding:, context_obj:) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/irb/ai.rb', line 46 def execute_expression(expression:, context_binding:, context_obj:) output = StringIO.new exception = nil return_value = IRB::AI::NULL_VALUE ObjectTracer .new( context_obj, output: output, colorize: false, header: "object-trace" ) .start do ExceptionTracer .new(output: output, colorize: false, header: "exception-trace") .start do begin return_value = eval(expression, context_binding) rescue Exception => e exception = e end end end traces = output.string.split("\n") [return_value, exception, traces] end |
.generate_message(expression:, context_binding:, context_obj:, traces:, exception:, return_value:) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/irb/ai.rb', line 116 def ( expression:, context_binding:, context_obj:, traces:, exception:, return_value: ) information = information_section( expression: expression, traces: traces, exception: exception, context_binding: context_binding, context_obj: context_obj, return_value: return_value ) request = request_section(expression: expression, exception: exception) msg = <<~MSG ### Information #{information} ### Request #{request} MSG if AI.debug_mode? puts "==================== Message ====================" puts msg puts "=================================================" end msg end |
.information_section(expression:, context_binding:, context_obj:, traces:, exception:, return_value:) ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/irb/ai.rb', line 154 def information_section( expression:, context_binding:, context_obj:, traces:, exception:, return_value: ) msg = <<~MSG - The expression `#{expression}` returned `#{return_value}` (ignore if its value equals to `#{IRB::AI::NULL_VALUE}`) - Here are the runtime traces when running the expression is evaluated (ignore if blank): #{traces.join("\n")} - The execution happened in the context of the object #{context_obj} - If a trace has `object-trace` header, that means the trace is about the execution of this object - If you see no `object-trace`, that means the object is not involved in the execution. if that's the case, you can ignore the context object and the code around the binding when generating the response MSG if exception msg += <<~MSG - The execution caused the following exception: #{exception} (ignore if blank) - Exception backtrace is: #{exception.backtrace} - If you see multiple `exception-trace`, that means multiple exceptions were raised during the execution - But only the last trace is directly associated with the exception you see above - Use other exception traces to understand the execution flow in general and don't assume they have direct link to the exception above MSG else msg += <<~MSG - The execution DID NOT cause an exception - Please ignore all `exception-trace` MSG end msg end |
.model ⇒ Object
26 27 28 |
# File 'lib/irb/ai.rb', line 26 def model @model ||= "gpt-3.5-turbo" end |
.model=(model) ⇒ Object
22 23 24 |
# File 'lib/irb/ai.rb', line 22 def model=(model) @model end |
.register_irb_commands ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/irb/ai.rb', line 30 def register_irb_commands ec = IRB::ExtendCommandBundle.instance_variable_get(:@EXTEND_COMMANDS) [ [ :explain, :Explain, nil, [:explain, IRB::ExtendCommandBundle::OVERRIDE_ALL] ] ].each do |ecconfig| ec.push(ecconfig) IRB::ExtendCommandBundle.def_extend_command(*ecconfig) end end |
.request_section(expression:, exception:) ⇒ Object
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/irb/ai.rb', line 192 def request_section(expression:, exception:) msg = <<~MSG Please respond in the following format, with markdown syntax, and use code highlight when appropriate: ``` This is an analysis of the program's behaviour when running the expression `#{expression}` ### Code Summary (skip if no program source code is given) <summary of the program's intended behaviour from the given code (ignore the breakpoint)> ### Execution Summary <summary of the program's actual behaviour from the given traces> <unless the program failed due to an exception, DO NOT assume the program failed because of the `exception-trace`s> ### Execution Details <detailed step-by-step explanation of the program's actual behaviour based on the source code and given trace> <strictly follow the below format> <example> 1. The program started at line 1 2. The program called method `foo` at line 2 3. The program called method `bar` at line 3 4. The trace shows that the program raised an exception at line 4 </example> MSG msg += if exception <<~MSG ### Debugging Suggestion for #{exception} <potential causes of the error based on the program's execution details as explained in the previous section> <if you think the information is not sufficient, please explicit mention that and explain what information is missing> MSG else <<~MSG <DO NOT assume the program failed due to an exception> MSG end msg end |
.send_messages(expression:, context_binding:, context_obj:, traces:, exception:, return_value:) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/irb/ai.rb', line 74 def ( expression:, context_binding:, context_obj:, traces:, exception:, return_value: ) = [ { role: "system", content: "I will help you understand your Ruby application's behaviour." }, { role: "user", content: ( expression: expression, traces: traces, exception: exception, context_binding: context_binding, context_obj: context_obj, return_value: return_value ) } ] puts "Getting response from OpenAI..." response = AI.ai_client.chat(parameters: { model: AI.model, messages: }) if AI.debug_mode? puts "==================== Raw Response ====================" pp response puts "======================================================" end response end |