Skip to content

Commit

Permalink
Remove the concept of Agent
Browse files Browse the repository at this point in the history
As I add more features, Agent would inevitably be bloated with methods, or I would need to create different kinds of agents. So I removed it in favour of commands, which are self-explanatory. Also, there's one less concept to learn
  • Loading branch information
wilsonsilva committed Apr 12, 2023
1 parent 2aa92b4 commit 59bd32e
Show file tree
Hide file tree
Showing 24 changed files with 235 additions and 178 deletions.
2 changes: 1 addition & 1 deletion .rubocop.yml
Expand Up @@ -12,7 +12,7 @@ AllCops:
# --------------------- Security ----------------------
Security/Eval:
Exclude:
- lib/senior/agent.rb
- lib/senior/commands/auto_debug_method.rb

# ----------------------- Style -----------------------

Expand Down
8 changes: 4 additions & 4 deletions README.md
Expand Up @@ -52,24 +52,24 @@ is excluded from source control.
Once you have configured the gem, you can use the `Senior` module to interact with the OpenAI API.

### Auto-debugging a broken method
To debug a broken method, call `Senior.auto_debug` and pass in the broken method and its arguments. The method will be
To debug a broken method, call `Senior.auto_debug_method` and pass in the broken method and its arguments. The method will be
called repeatedly, with modifications made to its source code each time, until it no longer raises exceptions.

```ruby
def square(n) = n * y

result = Senior.auto_debug(method(:square), 2)
result = Senior.auto_debug_method(method(:square), 2)
puts result # => 4
```

### Suggesting a fix for a broken method
To suggest a fix for a broken method, call `Senior.suggest_fix` and pass in the broken method and its arguments.
To suggest a fix for a broken method, call `Senior.suggest_method_fix` and pass in the broken method and its arguments.
The method will be analyzed and a fix will be suggested in the form of modified source code.

```ruby
def square(n) = n * y

suggestion = Senior.suggest_fix(method(:square), 2)
suggestion = Senior.suggest_method_fix(method(:square), 2)
puts suggestion # => "def square(n) = n * n"
```

Expand Down
23 changes: 12 additions & 11 deletions lib/senior.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true

require_relative 'senior/errors'
require_relative 'senior/commands/auto_debug_method'
require_relative 'senior/commands/suggest_method_fix'
require_relative 'senior/configuration/main'
require_relative 'senior/brains/open_ai'
require_relative 'senior/agent'
require_relative 'senior/version'

require 'method_source'
Expand All @@ -17,7 +18,7 @@ module Senior
# @example Debugging a broken method
# def square(n) = n * y
#
# result = Senior.auto_debug(method(:square), 2)
# result = Senior.auto_debug_method(method(:square), 2)
# result # => 4
#
# @param broken_method [Method] A broken method to be fixed
Expand All @@ -26,8 +27,8 @@ module Senior
#
# @return [Object] The return value of the previously broken but now fixed method
#
def self.auto_debug(broken_method, args, broken_method_source = nil)
agent.auto_debug(broken_method, args, broken_method_source)
def self.auto_debug_method(broken_method, args, broken_method_source = nil)
Commands::AutoDebugMethod.new(brain).call(broken_method, args, broken_method_source)
end

# Suggests a fix for a broken method
Expand All @@ -37,26 +38,26 @@ def self.auto_debug(broken_method, args, broken_method_source = nil)
# @example Suggesting a fix for a broken method
# def square(n) = n * y
#
# suggestion = Senior.suggest_fix(method(:square), 2)
# suggestion = Senior.suggest_method_fix(method(:square), 2)
# suggestion # => "def square(n) = n * n"
#
# @param broken_method [Method] A broken method to be fixed
# @param args [Object] Arguments given to a broken method
#
# @return [String] The suggested fix
#
def self.suggest_fix(broken_method, args)
agent.suggest_fix(broken_method, args)
def self.suggest_method_fix(broken_method, args)
Commands::SuggestMethodFix.new(brain).call(broken_method, args)
end

# Returns an instance of the agent
# Returns an interface to OpenAI's API
#
# @api private
#
# @return [Agent] An instance of the agent
# @return [Brain] Interface to OpenAI's API
#
def self.agent
@agent ||= Agent.new
def self.brain
@brain ||= Brains::OpenAI.new
end

# Returns the configuration object for the Senior gem
Expand Down
72 changes: 0 additions & 72 deletions lib/senior/agent.rb

This file was deleted.

2 changes: 1 addition & 1 deletion lib/senior/brains/open_ai.rb
Expand Up @@ -24,7 +24,7 @@ class OpenAI
#
# @return [String] The suggested fix
#
def suggest_fix(erroneous_source:, exception_backtrace:)
def suggest_method_fix(erroneous_source:, exception_backtrace:)
prompt = <<~PROMPT
This is a method's source and the error. Fix the method:
Expand Down
58 changes: 58 additions & 0 deletions lib/senior/commands/auto_debug_method.rb
@@ -0,0 +1,58 @@
# frozen_string_literal: true

module Senior
module Commands
# Auto debugs a given method using AI to suggest fixes until the method no longer raises exceptions
#
# @api private
#
class AutoDebugMethod
# Instantiates a new AutoDebugMethod
#
# @api private
#
# @param brain [Object] The interface to an AI's API
#
def initialize(brain = Brains::OpenAI.new)
@brain = brain
end

# Calls the given method continuously, using AI to attempt to fix it until it no longer raises exceptions
#
# @api private
#
# @param broken_method [Method] A broken method to be fixed
# @param args [Object] Arguments given to a broken method
# @param broken_method_source [String|nil] Source code of the broken method
#
# @return [Object] The return value of the previously broken but now fixed method
#
def call(broken_method, args, broken_method_source = nil)
broken_method.call(*args)
rescue StandardError => e
puts "The invocation #{broken_method.name}(#{args}) failed. Debugging..."

suggested_fix_method_source = brain.suggest_method_fix(
erroneous_source: broken_method_source || broken_method.source,
exception_backtrace: e.backtrace&.first.to_s
)

puts "\nSuggested fix:\n#{suggested_fix_method_source}\n\n"

suggested_fix_method_name = eval(suggested_fix_method_source)

call(method(suggested_fix_method_name), args, suggested_fix_method_source)
end

private

# The interface to an AI's API
#
# @api private
#
# @return [Object]
#
attr_reader :brain
end
end
end
49 changes: 49 additions & 0 deletions lib/senior/commands/suggest_method_fix.rb
@@ -0,0 +1,49 @@
# frozen_string_literal: true

module Senior
module Commands
# Suggests a fix for a broken method
#
# @api private
#
class SuggestMethodFix
# Instantiates a new SuggestMethodFix
#
# @api private
#
# @param brain [Object] The interface to an AI's API
#
def initialize(brain = Brains::OpenAI.new)
@brain = brain
end

# Suggests a fix for a broken method
#
# @api private
#
# @param broken_method [Method] A broken method to be fixed
# @param args [Object] Arguments given to a broken method
#
# @return [String] The suggested fix
#
def call(broken_method, args)
broken_method.call(*args)
rescue StandardError => e
brain.suggest_method_fix(
erroneous_source: broken_method.source,
exception_backtrace: e.backtrace&.first.to_s
)
end

private

# The interface to an AI's API
#
# @api private
#
# @return [Object]
#
attr_reader :brain
end
end
end
9 changes: 5 additions & 4 deletions sig/senior.rbs
@@ -1,11 +1,12 @@
module Senior
VERSION: String

self.@configuration: Configuration::Main
self.@agent: Agent
self.@brain: _IBrain

def self.auto_debug: (Method broken_method, untyped args, String? | nil broken_method_source) -> untyped
def self.suggest_fix: (Method broken_method, untyped args) -> String
def self.auto_debug_method: (Method broken_method, untyped args, String? | nil broken_method_source) -> untyped
def self.suggest_method_fix: (Method broken_method, untyped args) -> String
def self.configuration: -> Configuration::Main
def self.configure: -> untyped
def self.agent: -> Agent
def self.brain: -> _IBrain
end
11 changes: 0 additions & 11 deletions sig/senior/agent.rbs

This file was deleted.

2 changes: 1 addition & 1 deletion sig/senior/brains/i_brain.rbs
@@ -1,3 +1,3 @@
interface _IBrain
def suggest_fix: (erroneous_source: String, exception_backtrace: String) -> String
def suggest_method_fix: (erroneous_source: String, exception_backtrace: String) -> String
end
2 changes: 1 addition & 1 deletion sig/senior/brains/open_ai.rbs
Expand Up @@ -7,7 +7,7 @@ module Senior

@open_ai_client: untyped

def suggest_fix: (erroneous_source: String, exception_backtrace: String) -> String
def suggest_method_fix: (erroneous_source: String, exception_backtrace: String) -> String

private

Expand Down
14 changes: 14 additions & 0 deletions sig/senior/commands/auto_debug_method.rbs
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module Senior
module Commands
class AutoDebugMethod
def initialize: (?_IBrain brain) -> void
def call: (Method broken_method, untyped args, String? | nil broken_method_source) -> untyped

private

attr_reader brain: _IBrain
end
end
end
14 changes: 14 additions & 0 deletions sig/senior/commands/suggest_method_fix.rbs
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module Senior
module Commands
class SuggestMethodFix
def initialize: (?_IBrain brain) -> void
def call: (Method broken_method, untyped args) -> String

private

attr_reader brain: _IBrain
end
end
end

0 comments on commit 59bd32e

Please sign in to comment.