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 +4 -4
- data/lib/ruby_bots/bot.rb +1 -1
- data/lib/ruby_bots/bots/openai_bot.rb +13 -2
- data/lib/ruby_bots/bots/openai_react_bot.rb +132 -0
- data/lib/ruby_bots/bots/pipeline_bot.rb +3 -3
- data/lib/ruby_bots/bots/router_bot.rb +3 -3
- data/lib/ruby_bots/tool.rb +1 -1
- data/lib/ruby_bots/tools/openai_tool.rb +2 -2
- data/lib/ruby_bots/tools/wolfram_tool.rb +24 -0
- data/lib/ruby_bots/version.rb +1 -1
- data/lib/ruby_bots.rb +8 -6
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f033f968d4ac24541d10ae38913ea8752d1c6a08c538b3bab6716bcd72365e7a
|
4
|
+
data.tar.gz: c1f905d49c704475e7c96f0ab8957ac78cf422fc6fdfd38e0b15c0a2e3fe4671
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b361259f543091c9a252419bcbe9d07a839b4b92249f790d58e8f09c7d421e62225d1e4f704d5dbad2932fb436f3668215c8e4f863550d2802475c00d776b46b
|
7
|
+
data.tar.gz: 011546a87f231642929daaf6a6edcd9a483da7961992958f30a0dfa8f402125c843860466c270407986c665d1a0e557a1e21652a543175374991cee1f68741b7
|
data/lib/ruby_bots/bot.rb
CHANGED
@@ -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(
|
25
|
+
def run(input)
|
15
26
|
params = {
|
16
27
|
messages: [
|
17
28
|
{ role: :system, content: system_instructions },
|
18
|
-
{ role: :user, content:
|
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
|
@@ -19,11 +19,11 @@ module RubyBots
|
|
19
19
|
|
20
20
|
private
|
21
21
|
|
22
|
-
def run(
|
22
|
+
def run(input)
|
23
23
|
params = {
|
24
24
|
messages: [
|
25
25
|
{ role: :system, content: system_instructions },
|
26
|
-
{ role: :user, content:
|
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(
|
35
|
+
selected_tool.response(input)
|
36
36
|
else
|
37
37
|
response_text
|
38
38
|
end
|
data/lib/ruby_bots/tool.rb
CHANGED
@@ -25,11 +25,11 @@ module RubyBots
|
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
-
def run(
|
28
|
+
def run(input)
|
29
29
|
params = {
|
30
30
|
messages: [
|
31
31
|
{ role: :system, content: system_instructions },
|
32
|
-
{ role: :user, content:
|
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
|
data/lib/ruby_bots/version.rb
CHANGED
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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.
|
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-
|
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:
|