ruby_bots 0.0.21 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76d6f4b77a8ef6bc0f8124475142c2fb90460e9c9f602381eca42488aea99ea3
4
- data.tar.gz: bf11eed995aec29f80a3f97f86fc1856f6c4733cdbe7d716cf705af0f9733ad4
3
+ metadata.gz: f033f968d4ac24541d10ae38913ea8752d1c6a08c538b3bab6716bcd72365e7a
4
+ data.tar.gz: c1f905d49c704475e7c96f0ab8957ac78cf422fc6fdfd38e0b15c0a2e3fe4671
5
5
  SHA512:
6
- metadata.gz: 7c052e9aad0d5de43e0ab7644b15ded54a84b1c87429a7780c455d80e93239e15a878a4d015eb9dde9383e300132d661e6e72218dff79b2ac8855117ab2aade3
7
- data.tar.gz: 7fd242669b9332dec10d886ece3145ea3b76420459b5fb532e502f3050803d90cb839e6bb5e65711e6d79dc221a6dc6e5d9b5c115e019ead934bc8a877d8fd2d
6
+ metadata.gz: b361259f543091c9a252419bcbe9d07a839b4b92249f790d58e8f09c7d421e62225d1e4f704d5dbad2932fb436f3668215c8e4f863550d2802475c00d776b46b
7
+ data.tar.gz: 011546a87f231642929daaf6a6edcd9a483da7961992958f30a0dfa8f402125c843860466c270407986c665d1a0e557a1e21652a543175374991cee1f68741b7
data/lib/ruby_bots/bot.rb CHANGED
@@ -9,7 +9,7 @@ module RubyBots
9
9
 
10
10
  private
11
11
 
12
- def run(inputs)
12
+ def run(input)
13
13
  raise NotImplementedError
14
14
  end
15
15
  end
@@ -9,13 +9,24 @@ module RubyBots
9
9
  super(name: name, description: description)
10
10
  end
11
11
 
12
+ def system_instructions
13
+ <<~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
+
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.
20
+ PROMPT
21
+ end
22
+
12
23
  private
13
24
 
14
- def run(inputs)
25
+ def run(input)
15
26
  params = {
16
27
  messages: [
17
28
  { role: :system, content: system_instructions },
18
- { role: :user, content: inputs }
29
+ { role: :user, content: input }
19
30
  ]
20
31
  }.merge(default_params)
21
32
 
@@ -0,0 +1,132 @@
1
+ module RubyBots
2
+ class OpenAIReactBot < OpenAIBot
3
+ attr_accessor :tools
4
+
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
+ ]
11
+
12
+ def initialize(name: "OpenAI ReAct bot", description: DEFAULT_DESCRIPTION, tools: DEFAULT_TOOLS)
13
+ @tools = tools
14
+ super(name: name, description: description)
15
+ end
16
+
17
+ def examples
18
+ [ EXAMPLE_ONE, EXAMPLE_TWO, EXAMPLE_THREE ]
19
+ end
20
+
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
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
62
+ EXAMPLE
63
+
64
+ def system_instructions
65
+ <<~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") }
70
+
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.
75
+
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.
78
+
79
+ Examples:
80
+ #{ examples.join("\n\n") }
81
+ PROMPT
82
+ end
83
+
84
+ 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
+
94
+ response = client.chat(parameters: params)
95
+ response = response.dig("choices", 0, "message", "content")
96
+
97
+ while !is_answer?(response)
98
+ 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")
107
+ end
108
+
109
+ get_answer_from_response(response)
110
+ end
111
+
112
+ def is_answer?(input)
113
+ input.match(/Answer: /)
114
+ end
115
+
116
+ 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
+
121
+ notify_observers(:action, tool_name, tool_input)
122
+
123
+ tool = tools.find{|t| t.name == tool_name}
124
+
125
+ tool.response(tool_input)
126
+ end
127
+
128
+ def get_answer_from_response(response)
129
+ response.match(/Answer: ([\s\S]*)/)[1]
130
+ end
131
+ end
132
+ end
@@ -8,12 +8,12 @@ module RubyBots
8
8
 
9
9
  private
10
10
 
11
- def run(inputs)
11
+ def run(input)
12
12
  tools.each do |tool|
13
- inputs = tool.run(inputs)
13
+ input = tool.run(input)
14
14
  end
15
15
 
16
- inputs
16
+ input
17
17
  end
18
18
  end
19
19
  end
@@ -19,11 +19,11 @@ module RubyBots
19
19
 
20
20
  private
21
21
 
22
- def run(inputs)
22
+ def run(input)
23
23
  params = {
24
24
  messages: [
25
25
  { role: :system, content: system_instructions },
26
- { role: :user, content: inputs }
26
+ { role: :user, content: input }
27
27
  ]
28
28
  }.merge(default_params)
29
29
 
@@ -32,7 +32,7 @@ module RubyBots
32
32
  response_text = response.dig("choices", 0, "message", "content")
33
33
  selected_tool = tools.find{|t| t.name == response_text}
34
34
  if selected_tool
35
- selected_tool.response(inputs)
35
+ selected_tool.response(input)
36
36
  else
37
37
  response_text
38
38
  end
@@ -48,7 +48,7 @@ module RubyBots
48
48
 
49
49
  private
50
50
 
51
- def run(inputs)
51
+ def run(input)
52
52
  raise NotImplementedError
53
53
  end
54
54
 
@@ -25,11 +25,11 @@ module RubyBots
25
25
 
26
26
  private
27
27
 
28
- def run(inputs)
28
+ def run(input)
29
29
  params = {
30
30
  messages: [
31
31
  { role: :system, content: system_instructions },
32
- { role: :user, content: inputs }
32
+ { role: :user, content: input }
33
33
  ]
34
34
  }.merge(default_params)
35
35
 
@@ -0,0 +1,24 @@
1
+ require 'cgi'
2
+ require 'uri'
3
+ require 'net/http'
4
+
5
+ module RubyBots
6
+ 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
9
+
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)
14
+ end
15
+
16
+ private
17
+
18
+ def run(input)
19
+ uri = URI("http://api.wolframalpha.com/v1/simple?appid=#{ENV['WOLFRAM_APPID']}&i=#{CGI.escape(input)}")
20
+ resp = Net::HTTP.get_response(uri)
21
+ resp.body
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module RubyBots
2
- VERSION = "0.0.21".freeze
2
+ VERSION = "0.0.23".freeze
3
3
  end
data/lib/ruby_bots.rb CHANGED
@@ -13,6 +13,7 @@ module RubyBots
13
13
  attr_accessor :openai_api_key
14
14
  def initialize()
15
15
  @openai_api_key = ENV["OPENAI_ACCESS_TOKEN"]
16
+ @wolfram_app_id = ENV["WOLFRAM_APPID"]
16
17
  end
17
18
  end
18
19
 
@@ -29,9 +30,10 @@ module RubyBots
29
30
  class InvalidOutputError < Error; end
30
31
  end
31
32
 
32
- require "ruby_bots/tool"
33
- require "ruby_bots/bot"
34
- require "ruby_bots/tools/openai_tool.rb"
35
- require "ruby_bots/bots/openai_bot.rb"
36
- require "ruby_bots/bots/pipeline_bot.rb"
37
- require "ruby_bots/bots/router_bot.rb"
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"
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.21
4
+ version: 0.0.23
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-03 00:00:00.000000000 Z
11
+ date: 2023-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: debug
@@ -91,10 +91,12 @@ files:
91
91
  - lib/ruby_bots.rb
92
92
  - lib/ruby_bots/bot.rb
93
93
  - lib/ruby_bots/bots/openai_bot.rb
94
+ - lib/ruby_bots/bots/openai_react_bot.rb
94
95
  - lib/ruby_bots/bots/pipeline_bot.rb
95
96
  - lib/ruby_bots/bots/router_bot.rb
96
97
  - lib/ruby_bots/tool.rb
97
98
  - lib/ruby_bots/tools/openai_tool.rb
99
+ - lib/ruby_bots/tools/wolfram_tool.rb
98
100
  - lib/ruby_bots/version.rb
99
101
  homepage: https://github.com/aha-app/ruby_bots
100
102
  licenses: