ruby_bots 0.0.23 → 0.0.25

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f033f968d4ac24541d10ae38913ea8752d1c6a08c538b3bab6716bcd72365e7a
4
- data.tar.gz: c1f905d49c704475e7c96f0ab8957ac78cf422fc6fdfd38e0b15c0a2e3fe4671
3
+ metadata.gz: 7500351293df6637a79657ff218f06faa376a9a35104e11cf8d7f232af2de1d0
4
+ data.tar.gz: 1fbcb4915525bc159649c098ae66c02cdb41a1ae15941e709d5073327026559a
5
5
  SHA512:
6
- metadata.gz: b361259f543091c9a252419bcbe9d07a839b4b92249f790d58e8f09c7d421e62225d1e4f704d5dbad2932fb436f3668215c8e4f863550d2802475c00d776b46b
7
- data.tar.gz: 011546a87f231642929daaf6a6edcd9a483da7961992958f30a0dfa8f402125c843860466c270407986c665d1a0e557a1e21652a543175374991cee1f68741b7
6
+ metadata.gz: b76904f26db1b284a6ebc7cd262dcc9d86276afaead350bff6f61964b633d05314f857f3128996b8395fc7cd2df04eab796af9fd81cfef0a43008625d86f89e7
7
+ data.tar.gz: e685e6fa6df00ceb1c89bceeadf15a2ca06f8ebac02b1b5f7941fbedfbedf98ca5ad0093c300707d25a5fef26203fcc990ccc9339817b9ee260d56bdb80e8790
data/lib/ruby_bots/bot.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  module RubyBots
2
+ # Base class for all RubyBots bots
2
3
  class Bot < Tool
3
4
  attr_accessor :tools
4
5
 
5
- def initialize(name:, description:, tools:)
6
+ def initialize(tools:, name: 'RubyBots Bot', description: 'This is a RubyBots bot')
6
7
  @tools = tools
7
- super(name: name, description: description)
8
+ super(name:, description:)
8
9
  end
9
10
 
10
11
  private
@@ -12,5 +13,9 @@ module RubyBots
12
13
  def run(input)
13
14
  raise NotImplementedError
14
15
  end
16
+
17
+ def tool_name?(param)
18
+ errors << 'invalid tool name' unless @tools.map(&:name).include?(param)
19
+ end
15
20
  end
16
- end
21
+ end
@@ -1,38 +1,45 @@
1
1
  module RubyBots
2
+ # Bot class for OpenAI. This will use open AI to determine which tool to use for the response.
2
3
  class OpenAIBot < OpenAITool
3
4
  attr_accessor :tools
4
5
 
5
- DEFAULT_DESCRIPTION = "This bot will use OpenAI to determine the appropriate tool."
6
-
7
- def initialize(name: "OpenAI bot", description: DEFAULT_DESCRIPTION, tools:)
6
+ validate_output :tool_name?
7
+
8
+ DEFAULT_DESCRIPTION = 'This bot will use OpenAI to determine the appropriate tool.'.freeze
9
+
10
+ def initialize(tools:, name: 'OpenAI bot', description: DEFAULT_DESCRIPTION)
8
11
  @tools = tools
9
- super(name: name, description: description)
12
+ super(name:, description:)
10
13
  end
11
14
 
12
15
  def system_instructions
13
16
  <<~PROMPT
14
- You are an assistant designed to select a tool for a user to use. You are provided with the user's input.
15
- Select from the following tools (name - description):
16
- #{tools.map{|t| t.name + " - " + t.description}.join("\n")}
17
+ You are an assistant designed to select a tool for a user to use. You are provided with the user's input.
18
+ Select from the following tools (name - description):
19
+ #{tools.map { |t| "#{t.name} - #{t.description}" }.join('\n')}
17
20
 
18
- Return only the name of the tool that best fits the user's request.
19
- If no tools match the user's request respond with "no tool" and nothing more.
21
+ Return only the name of the tool that best fits the user's request.
22
+ If no tools match the user's request respond with "no tool" and nothing more.
20
23
  PROMPT
21
24
  end
22
25
 
23
26
  private
24
-
27
+
25
28
  def run(input)
26
29
  params = {
27
30
  messages: [
28
31
  { role: :system, content: system_instructions },
29
32
  { role: :user, content: input }
30
33
  ]
31
- }.merge(default_params)
34
+ }.merge(default_params)
32
35
 
33
36
  response = client.chat(parameters: params)
34
37
 
35
- response.dig("choices", 0, "message", "content")
38
+ response.dig('choices', 0, 'message', 'content')
39
+ end
40
+
41
+ def tool_name?(param)
42
+ errors << 'invalid tool name' unless @tools.map(&:name).include?(param)
36
43
  end
37
44
  end
38
- end
45
+ end
@@ -1,126 +1,129 @@
1
1
  module RubyBots
2
+ # This bot uses the ReAct framework to select from the provide tools and respond to the user.
3
+ # The input from the tools that are called by the bot are fed back into the messages as observations.
2
4
  class OpenAIReactBot < OpenAIBot
3
5
  attr_accessor :tools
4
6
 
5
- DEFAULT_DESCRIPTION = "This bot will use the ReAct framework to determine the appropriate response. It is powered by OpenAI and the ReAct framework."
6
-
7
- DEFAULT_TOOLS = [
8
- RubyBots::Tool.new(name:"search", description:"Search the web for information. Input should be the string to search for."),
9
- RubyBots::Tool.new(name:"calculate", description:"Solve math problems. Input should be a mathematical expression with no additional details or context."),
10
- ]
7
+ DEFAULT_DESCRIPTION = <<~DESCRIPTION.strip_heredoc
8
+ This bot will use the ReAct framework to determine the appropriate response. It is powered by OpenAI and the ReAct framework.
9
+ DESCRIPTION
11
10
 
12
- def initialize(name: "OpenAI ReAct bot", description: DEFAULT_DESCRIPTION, tools: DEFAULT_TOOLS)
13
- @tools = tools
14
- super(name: name, description: description)
11
+ DEFAULT_TOOLS = [
12
+ RubyBots::Tool.new(
13
+ name: 'search',
14
+ description: 'Search the web for information. Input should be the string to search for.'
15
+ ),
16
+ RubyBots::Tool.new(
17
+ name: 'calculate',
18
+ description: <<~DESC.strip_heredoc
19
+ Solve math problems. Input should be a mathematical expression with no additional details or context.
20
+ DESC
21
+ )
22
+ ].freeze
23
+
24
+ def initialize(tools: DEFAULT_TOOLS, name: 'OpenAI ReAct bot', description: DEFAULT_DESCRIPTION)
25
+ super(tools:, name:, description:)
15
26
  end
16
27
 
17
28
  def examples
18
- [ EXAMPLE_ONE, EXAMPLE_TWO, EXAMPLE_THREE ]
29
+ [EXAMPLE_ONE, EXAMPLE_TWO]
19
30
  end
20
31
 
21
-
22
- EXAMPLE_ONE = <<~EXAMPLE
23
- User: What is the current temperature in the city Julia Roberts was born in?
24
- Thought: I need to know where Julia Roberts was born.
25
- Action: search[Julia Roberts birthplace]
26
- Observation: Smyrna, Georgia
27
- Thought: I need to know the current temperature in Smyrna, Georgia.
28
- Action: search[current temperature Smyrna, Georgia]
29
- Observation: 72 degrees Fahrenheit
30
- Thought: I need to tell the user the current temperature in the city Julia Roberts was born in.
31
- Answer: 72 degrees Fahrenheit
32
+ EXAMPLE_ONE = <<~EXAMPLE.strip_heredoc
33
+ User: What is the current temperature in the city Julia Roberts was born in?
34
+ Thought: I need to know where Julia Roberts was born.
35
+ Action: search[Julia Roberts birthplace]
36
+ Observation: Smyrna, Georgia
37
+ Thought: I need to know the current temperature in Smyrna, Georgia.
38
+ Action: search[current temperature Smyrna, Georgia]
39
+ Observation: 72 degrees Fahrenheit
40
+ Thought: I need to tell the user the current temperature in the city Julia Roberts was born in.
41
+ Answer: 72 degrees Fahrenheit
32
42
  EXAMPLE
33
-
34
- EXAMPLE_TWO = <<~EXAMPLE
35
- User: What is the square of the age of Albert Einstein at his death?
36
- Thought: I need to know the age of Albert Einstein at his death.
37
- Action: search[Albert Einstein age at death]
38
- Observation: 76 years old
39
- Thought: I need to know the square of 76.
40
- Action: calculate[76^2]
41
- Observation: 5776
42
- Thought: I need to tell the user the square of the age of Albert Einstein at his death.
43
- Answer: 5776
44
- EXAMPLE
45
-
46
- EXAMPLE_THREE = <<~EXAMPLE
47
- User: What is half of the amount of years that have passed since the year the first airplane flew?
48
- Thought: I need to know the year the first airplane flew.
49
- Action: search[first airplane flight year]
50
- Observation: 1903
51
- Thought: I need to know the current year.
52
- Action: search[current year]
53
- Observation: 2023
54
- Thought: I need to know the amount of years that have passed since 1903.
55
- Action: calculate[2023 - 1903]
56
- Observation: 120
57
- Thought: I need to know half of 120.
58
- Action: calculate[120 / 2]
59
- Observation: 60
60
- Thought: I need to tell the user half of the amount of years that have passed since the year the first airplane flew.
61
- Answer: 60
43
+
44
+ EXAMPLE_TWO = <<~EXAMPLE.strip_heredoc
45
+ User: What is half of the amount of years that have passed since the year the first airplane flew?
46
+ Thought: I need to know the year the first airplane flew.
47
+ Action: search[first airplane flight year]
48
+ Observation: 1903
49
+ Thought: I need to know the current year.
50
+ Action: search[current year]
51
+ Observation: 2023
52
+ Thought: I need to know the amount of years that have passed since 1903.
53
+ Action: calculate[2023 - 1903]
54
+ Observation: 120
55
+ Thought: I need to know half of 120.
56
+ Action: calculate[120 / 2]
57
+ Observation: 60
58
+ Thought: I need to tell the user half of the amount of years that have passed since the year the first airplane flew.
59
+ Answer: 60
62
60
  EXAMPLE
63
61
 
62
+ def prefix
63
+ <<~PROMPT.strip_heredoc
64
+ You are an assistant designed to provide solutions for a user. You are provided with the user's input.
65
+ PROMPT
66
+ end
67
+
68
+ def suffix
69
+ ''
70
+ end
71
+
64
72
  def system_instructions
65
73
  <<~PROMPT
66
- You are an assistant designed to provide solutions for a user. You are provided with the user's input.
67
- You run in a loop of thought, action, observation, and reflection until you have a solution for the user.
68
- You can utilize the following actions to gather more information (name - description):
69
- #{ tools.map{ |t| t.name + " - " + t.description }.join("\n") }
74
+ #{prefix}
75
+
76
+ You run in a loop of thought, action, observation, and reflection until you have a solution for the user.
77
+ You can utilize the following actions (name - description):
78
+ #{tools.map { |t| "#{t.name} - #{t.description}" }.join("\n")}
79
+
80
+ You should begin by providing a thought and an action with the necessary input for the action. The action will be executed externally, and then you will be called again with the observation returned from the action.
81
+
82
+ You should then begin the loop again and provide a thought and action.
70
83
 
71
- You should begin by providing a thought and an action with the necessary input for the action. The action will be
72
- executed externally, and then you will be called again with the observation returned from the action.
73
-
74
- You should then begin the loop again and provide a thought and action.
84
+ If you have a solution for the user, return the solution instead of a new action.
85
+ The final answer should only answer the question without additional information about reasoning or process.
75
86
 
76
- If you have a solution for the user, return the solution instead of a new action.
77
- The final answer should only answer the question without additional information about reasoning or process.
87
+ #{suffix}
78
88
 
79
- Examples:
80
- #{ examples.join("\n\n") }
89
+ Examples:
90
+ #{examples.join("\n\n")}
81
91
  PROMPT
82
92
  end
83
93
 
84
94
  private
85
-
86
- def run(input)
87
- params = {
88
- messages: [
89
- { role: :system, content: system_instructions },
90
- { role: :user, content: input }
91
- ]
92
- }.merge(default_params)
93
95
 
94
- response = client.chat(parameters: params)
95
- response = response.dig("choices", 0, "message", "content")
96
+ def run(input)
97
+ params = initial_params(input)
96
98
 
97
- while !is_answer?(response)
99
+ until answer?(response = client.chat(parameters: params).dig('choices', 0, 'message', 'content'))
98
100
  params[:messages] << { role: :assistant, content: response }
99
-
100
- observation = get_observation_from_action(response)
101
-
102
- params[:messages] << { role: :assistant, content: "Observation: #{observation}" }
103
-
104
- response = client.chat(parameters: params)
105
-
106
- response = response.dig("choices", 0, "message", "content")
101
+ params[:messages] << { role: :assistant, content: "Observation: #{get_observation_from_action(response)}" }
107
102
  end
108
-
109
103
  get_answer_from_response(response)
110
104
  end
111
105
 
112
- def is_answer?(input)
106
+ def initial_params(input)
107
+ {
108
+ messages: [
109
+ { role: :system, content: system_instructions },
110
+ { role: :user, content: input }
111
+ ]
112
+ }.merge(default_params)
113
+ end
114
+
115
+ def answer?(input)
113
116
  input.match(/Answer: /)
114
117
  end
115
118
 
116
119
  def get_observation_from_action(response)
117
- t = response.match(/Action: ([\s\S]*)/)[1]
118
- tool_name = t.match(/([a-zA-Z\-\_]*)\[([\s\S]*)\]/)[1]
119
- tool_input = t.match(/([a-zA-Z\-\_]*)\[([\s\S]*)\]/)[2]
120
+ tool_string = response.match(/Action: ([\s\S]*)/)[1]
121
+ tool_name = tool_string.match(/([a-zA-Z_\- ]*)\[([\s\S]*)\]/)[1]
122
+ tool_input = tool_string.match(/([a-zA-Z_\- ]*)\[([\s\S]*)\]/)[2]
120
123
 
121
124
  notify_observers(:action, tool_name, tool_input)
122
125
 
123
- tool = tools.find{|t| t.name == tool_name}
126
+ tool = tools.find { |t| t.name == tool_name }
124
127
 
125
128
  tool.response(tool_input)
126
129
  end
@@ -1,13 +1,18 @@
1
1
  module RubyBots
2
+ # Class to provide a pipeline of tools to solve a problem.
3
+ # This bot will call the tools provided in order until each tool has responded.
4
+ # The bot will respond with the last tool's response.
2
5
  class PipelineBot < Bot
3
- DEFAULT_DESCRIPTION = "This bot will utilize all of its tools in a syncronouse pipeline based on the order the tools are provided."
4
-
5
- def initialize(name: "Pipeline bot", description: DEFAULT_DESCRIPTION, tools:)
6
- super(name: name, description: description, tools: tools)
6
+ DEFAULT_DESCRIPTION = <<~DESCRIPTION.strip_heredoc
7
+ This bot will utilize all of its tools in a syncronouse pipeline based on the order the tools are provided."
8
+ DESCRIPTION
9
+
10
+ def initialize(tools:, name: 'Pipeline bot', description: DEFAULT_DESCRIPTION)
11
+ super(tools:, name:, description:)
7
12
  end
8
13
 
9
14
  private
10
-
15
+
11
16
  def run(input)
12
17
  tools.each do |tool|
13
18
  input = tool.run(input)
@@ -16,4 +21,4 @@ module RubyBots
16
21
  input
17
22
  end
18
23
  end
19
- end
24
+ end
@@ -1,41 +1,49 @@
1
1
  module RubyBots
2
+ # Class to provide a router to different tools.
3
+ # This bot connects to the OpenAI API and uses gpt-4 by default to choose a proper tool to route the user's input.
4
+ # The bot will only select and use one tool by default.
2
5
  class RouterBot < OpenAIBot
3
- DEFAULT_DESCRIPTION = "This bot will route the user's input to the appropriate tool. It will only select and use one tool."
4
-
5
- def initialize(name: "Router bot", description: DEFAULT_DESCRIPTION, tools:)
6
- super(name: name, description: description, tools: tools)
6
+ DEFAULT_DESCRIPTION = <<~DESCRIPTION.strip_heredoc
7
+ This bot will route the user's input to the appropriate tool. It will only select and use one tool."
8
+ DESCRIPTION
9
+
10
+ def initialize(tools:, name: 'Router bot', description: DEFAULT_DESCRIPTION)
11
+ super(tools:, name:, description:)
7
12
  end
8
13
 
9
14
  def system_instructions
10
15
  <<~PROMPT
11
- You are an assistant helping to route a user's input to the correct tool.
12
- You can use the following tools (name - description):
13
- #{tools.map{|t| t.name + " - " + t.description}.join("\n")}
16
+ You are an assistant helping to route a user's input to the correct tool.
17
+ You can use the following tools (name - description):
18
+ #{tools.map { |t| "#{t.name} - #{t.description}" }.join('\n')}
14
19
 
15
- Return only the name of the tool that best fits the user's request.
16
- If no tools match the user's request respond with "I'm sorry, I am still learning." and nothing more.
20
+ Return only the name of the tool that best fits the user's request.
17
21
  PROMPT
18
22
  end
19
23
 
20
24
  private
21
-
25
+
22
26
  def run(input)
23
- params = {
27
+ params = initial_params(input)
28
+
29
+ response = client.chat(parameters: params)
30
+
31
+ response_text = response.dig('choices', 0, 'message', 'content')
32
+
33
+ selected_tool = tools.find { |t| t.name == response_text }
34
+
35
+ raise RubyBots::InvalidOutputError, 'invalid tool name' unless selected_tool
36
+
37
+ selected_tool.response(input)
38
+ end
39
+
40
+ def initial_params(input)
41
+ {
24
42
  messages: [
25
43
  { role: :system, content: system_instructions },
26
44
  { role: :user, content: input }
27
45
  ]
28
- }.merge(default_params)
29
-
30
- response = client.chat(parameters: params)
31
-
32
- response_text = response.dig("choices", 0, "message", "content")
33
- selected_tool = tools.find{|t| t.name == response_text}
34
- if selected_tool
35
- selected_tool.response(input)
36
- else
37
- response_text
38
- end
46
+ }.merge(default_params)
39
47
  end
40
48
  end
41
- end
49
+ end
@@ -1,4 +1,5 @@
1
1
  module RubyBots
2
+ # Base class for all RubyBots tools and bots
2
3
  class Tool
3
4
  attr_accessor :name, :description, :errors
4
5
 
@@ -23,41 +24,45 @@ module RubyBots
23
24
  end
24
25
 
25
26
  def response(input)
27
+ run_input_validations(input)
28
+
29
+ raise RubyBots::InvalidInputError, { errors: @errors } if @errors.any?
30
+
31
+ output = run(input)
32
+
33
+ run_output_validations(output)
34
+
35
+ raise RubyBots::InvalidOutputError, { errors: @errors } if @errors.any?
36
+
37
+ output
38
+ end
39
+
40
+ private
41
+
42
+ def run(input)
43
+ raise NotImplementedError
44
+ end
45
+
46
+ def run_input_validations(input)
26
47
  self.class.input_validators ||= []
27
48
  self.class.input_validators.each do |validator|
28
49
  send(validator, input)
29
50
  end
30
-
31
- if @errors.empty?
32
- output = run(input)
33
- else
34
- raise RubyBots::InvalidInputError.new(errors: @errors)
35
- end
36
-
51
+ end
52
+
53
+ def run_output_validations(output)
37
54
  self.class.output_validators ||= []
38
55
  self.class.output_validators.each do |validator|
39
56
  send(validator, output)
40
57
  end
41
-
42
- if @errors.any?
43
- raise RubyBots::InvalidOutputError.new(errors: @errors)
44
- end
45
-
46
- output
47
- end
48
-
49
- private
50
-
51
- def run(input)
52
- raise NotImplementedError
53
58
  end
54
59
 
55
- def is_json(param)
60
+ def json?(param)
56
61
  JSON.parse(param)
57
62
  rescue JSON::ParserError
58
- errors << "invalid JSON"
63
+ errors << 'invalid JSON'
59
64
  rescue TypeError
60
- errors << "value is not a String"
65
+ errors << 'value is not a String'
61
66
  end
62
67
  end
63
- end
68
+ end
@@ -1,41 +1,43 @@
1
1
  require 'openai'
2
2
 
3
3
  module RubyBots
4
+ # Tool for connecting to Open AI. Use this tool to connect to Open AI and get a response.
5
+ # the default model is gpt-4, it can be altered by overriding the default_params method.
4
6
  class OpenAITool < Tool
5
- DEFAULT_DESCRIPTION = "This tool will use open ai to determine the output."
6
-
7
- def initialize(name: "OpenAI Tool", description: DEFAULT_DESCRIPTION)
8
- super(name: name, description: description)
7
+ DEFAULT_DESCRIPTION = 'This tool will use open ai to determine the output.'.freeze
8
+
9
+ def initialize(name: 'OpenAI Tool', description: DEFAULT_DESCRIPTION)
10
+ super(name:, description:)
9
11
  end
10
-
12
+
11
13
  def client
12
14
  @client ||= OpenAI::Client.new(access_token: RubyBots.config.openai_api_key)
13
15
  end
14
-
16
+
15
17
  def default_params
16
18
  {
17
19
  model: 'gpt-4',
18
20
  temperature: 0
19
21
  }
20
22
  end
21
-
23
+
22
24
  def system_instructions
23
- "You are a helpful assistant."
25
+ 'You are a helpful assistant.'
24
26
  end
25
27
 
26
28
  private
27
-
29
+
28
30
  def run(input)
29
31
  params = {
30
32
  messages: [
31
33
  { role: :system, content: system_instructions },
32
34
  { role: :user, content: input }
33
35
  ]
34
- }.merge(default_params)
35
-
36
+ }.merge(default_params)
37
+
36
38
  response = client.chat(parameters: params)
37
-
38
- response.dig("choices", 0, "message", "content")
39
+
40
+ response.dig('choices', 0, 'message', 'content')
39
41
  end
40
42
  end
41
- end
43
+ end
@@ -3,14 +3,15 @@ require 'uri'
3
3
  require 'net/http'
4
4
 
5
5
  module RubyBots
6
+ # This tool provides an interface to get responses from the Wolfram Alpha API.
7
+ # This tool requires an App ID key from Wolfram Alpha.
8
+ # add it to your environment variables as WOLFRAM_APP_ID
9
+ # it is currently alpha and not fully working or tested.
6
10
  class WolframTool < Tool
7
- # This tool requires an App ID key from Wolfram Alpha.
8
- # add it to your environment variables as WOLFRAM_APP_ID
11
+ DEFAULT_DESCRIPTION = 'This tool will use the Wolfram Alpha API to answer questions.'.freeze
9
12
 
10
- DEFAULT_DESCRIPTION = "This tool will use the Wolfram Alpha API to answer questions."
11
-
12
- def initialize(name: "Wolfram tool", description: DEFAULT_DESCRIPTION)
13
- super(name: name, description: description)
13
+ def initialize(name: 'Wolfram tool', description: DEFAULT_DESCRIPTION)
14
+ super(name:, description:)
14
15
  end
15
16
 
16
17
  private
@@ -21,4 +22,4 @@ module RubyBots
21
22
  resp.body
22
23
  end
23
24
  end
24
- end
25
+ end
@@ -1,3 +1,3 @@
1
1
  module RubyBots
2
- VERSION = "0.0.23".freeze
3
- end
2
+ VERSION = '0.0.25'.freeze
3
+ end
data/lib/ruby_bots.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # Main module for RubyBots
1
2
  module RubyBots
2
3
  class << self
3
4
  def config
@@ -9,11 +10,13 @@ module RubyBots
9
10
  end
10
11
  end
11
12
 
13
+ # Configuration class
12
14
  class Configuration
13
15
  attr_accessor :openai_api_key
14
- def initialize()
15
- @openai_api_key = ENV["OPENAI_ACCESS_TOKEN"]
16
- @wolfram_app_id = ENV["WOLFRAM_APPID"]
16
+
17
+ def initialize
18
+ @openai_api_key = ENV['OPENAI_ACCESS_TOKEN']
19
+ @wolfram_app_id = ENV['WOLFRAM_APPID']
17
20
  end
18
21
  end
19
22
 
@@ -24,16 +27,27 @@ module RubyBots
24
27
  def self.tool
25
28
  RubyBots::Tool
26
29
  end
27
-
30
+
28
31
  class Error < StandardError; end
29
32
  class InvalidInputError < Error; end
30
33
  class InvalidOutputError < Error; end
31
34
  end
32
35
 
33
- require_relative "ruby_bots/tool"
34
- require_relative "ruby_bots/bot"
35
- require_relative "ruby_bots/tools/openai_tool.rb"
36
- require_relative "ruby_bots/bots/openai_bot.rb"
37
- require_relative "ruby_bots/bots/pipeline_bot.rb"
38
- require_relative "ruby_bots/bots/router_bot.rb"
39
- require_relative "ruby_bots/bots/openai_react_bot.rb"
36
+ class String
37
+ def strip_heredoc
38
+ gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, ''.freeze)
39
+ end
40
+ end
41
+
42
+ # tools
43
+ require_relative 'ruby_bots/tool'
44
+ require_relative 'ruby_bots/tools/openai_tool'
45
+
46
+ # bots
47
+ require_relative 'ruby_bots/bot'
48
+ require_relative 'ruby_bots/bots/openai_bot'
49
+ require_relative 'ruby_bots/bots/pipeline_bot'
50
+ require_relative 'ruby_bots/bots/router_bot'
51
+ require_relative 'ruby_bots/bots/openai_react_bot'
52
+
53
+ require_relative 'ruby_bots/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_bots
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.23
4
+ version: 0.0.25
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Paulson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-04-13 00:00:00.000000000 Z
11
+ date: 2023-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: debug
@@ -114,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
116
116
  - !ruby/object:Gem::Version
117
- version: 2.6.0
117
+ version: '3.1'
118
118
  required_rubygems_version: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - ">="