ruby_bots 0.0.20 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- 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 +131 -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 +3 -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 +3 -1
- 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: 9cb9f98a4334909eaa93b2c4f0ed5dcb3c92c85b32ecaeafcf17c2f1e8219a70
|
4
|
+
data.tar.gz: 978d6e24d899760d5c79c585ebbf6ee4bb93086c8e809a2d6befa145a74ddf03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44a634ac21766cabd85022c83c96d0672b1cbea6fea831932cf954b891cd310741e618b23cd4a0837e053bf4fb1c706fdc28a414ba7af2ccea451c57ad699d47
|
7
|
+
data.tar.gz: 8bbfe05922e5dd8e3016427e60aee9f10e443c811d6dfc1a57fb555d8b134eedd2f41bde6fe2b446315b0b4cfddd65e76d4b408932445190c042da4e4810a5db
|
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,131 @@
|
|
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
|
+
[
|
19
|
+
<<~EXAMPLE
|
20
|
+
User: What is the current temperature in the city Julia Roberts was born in?
|
21
|
+
Thought: I need to know where Julia Roberts was born.
|
22
|
+
Action: search[Julia Roberts birthplace]
|
23
|
+
Observation: Smyrna, Georgia
|
24
|
+
Thought: I need to know the current temperature in Smyrna, Georgia.
|
25
|
+
Action: search[current temperature Smyrna, Georgia]
|
26
|
+
Observation: 72 degrees Fahrenheit
|
27
|
+
Thought: I need to tell the user the current temperature in the city Julia Roberts was born in.
|
28
|
+
Answer: 72 degrees Fahrenheit
|
29
|
+
EXAMPLE
|
30
|
+
,
|
31
|
+
<<~EXAMPLE
|
32
|
+
User: What is the square of the age of Albert Einstein at his death?
|
33
|
+
Thought: I need to know the age of Albert Einstein at his death.
|
34
|
+
Action: search[Albert Einstein age at death]
|
35
|
+
Observation: 76 years old
|
36
|
+
Thought: I need to know the square of 76.
|
37
|
+
Action: calculate[76^2]
|
38
|
+
Observation: 5776
|
39
|
+
Thought: I need to tell the user the square of the age of Albert Einstein at his death.
|
40
|
+
Answer: 5776
|
41
|
+
EXAMPLE
|
42
|
+
,
|
43
|
+
<<~EXAMPLE
|
44
|
+
User: What is half of the amount of years that have passed since the year the first airplane flew?
|
45
|
+
Thought: I need to know the year the first airplane flew.
|
46
|
+
Action: search[first airplane flight year]
|
47
|
+
Observation: 1903
|
48
|
+
Thought: I need to know the current year.
|
49
|
+
Action: search[current year]
|
50
|
+
Observation: 2023
|
51
|
+
Thought: I need to know the amount of years that have passed since 1903.
|
52
|
+
Action: calculate[2023 - 1903]
|
53
|
+
Observation: 120
|
54
|
+
Thought: I need to know half of 120.
|
55
|
+
Action: calculate[120 / 2]
|
56
|
+
Observation: 60
|
57
|
+
Thought: I need to tell the user half of the amount of years that have passed since the year the first airplane flew.
|
58
|
+
Answer: 60
|
59
|
+
EXAMPLE
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
def system_instructions
|
64
|
+
<<~PROMPT
|
65
|
+
You are an assistant designed to provide solutions for a user. You are provided with the user's input.
|
66
|
+
You run in a loop of thought, action, observation, and reflection until you have a solution for the user.
|
67
|
+
You can utilize the following actions to gather more information (name - description):
|
68
|
+
#{ tools.map{ |t| t.name + " - " + t.description }.join("\n") }
|
69
|
+
|
70
|
+
You should begin by providing a thought and an action with the necessary input for the action. The action will be
|
71
|
+
executed externally, and then you will be called again with the observation returned from the action.
|
72
|
+
|
73
|
+
You should then begin the loop again and provide a thought and action.
|
74
|
+
|
75
|
+
If you have a solution for the user, return the solution instead of a new action.
|
76
|
+
The final answer should only answer the question without additional information about reasoning or process.
|
77
|
+
|
78
|
+
Examples:
|
79
|
+
#{ examples.join("\n\n") }
|
80
|
+
PROMPT
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def run(input)
|
86
|
+
params = {
|
87
|
+
messages: [
|
88
|
+
{ role: :system, content: system_instructions },
|
89
|
+
{ role: :user, content: input }
|
90
|
+
]
|
91
|
+
}.merge(default_params)
|
92
|
+
|
93
|
+
response = client.chat(parameters: params)
|
94
|
+
response = response.dig("choices", 0, "message", "content")
|
95
|
+
|
96
|
+
while !is_answer?(response)
|
97
|
+
params[:messages] << { role: :assistant, content: response }
|
98
|
+
|
99
|
+
observation = get_observation_from_action(response)
|
100
|
+
|
101
|
+
params[:messages] << { role: :assistant, content: "Observation: #{observation}" }
|
102
|
+
|
103
|
+
response = client.chat(parameters: params)
|
104
|
+
|
105
|
+
response = response.dig("choices", 0, "message", "content")
|
106
|
+
end
|
107
|
+
|
108
|
+
get_answer_from_response(response)
|
109
|
+
end
|
110
|
+
|
111
|
+
def is_answer?(input)
|
112
|
+
input.match(/Answer: /)
|
113
|
+
end
|
114
|
+
|
115
|
+
def get_observation_from_action(response)
|
116
|
+
t = response.match(/Action: ([\s\S]*)/)[1]
|
117
|
+
tool_name = t.match(/([a-zA-Z\-\_]*)\[([\s\S]*)\]/)[1]
|
118
|
+
tool_input = t.match(/([a-zA-Z\-\_]*)\[([\s\S]*)\]/)[2]
|
119
|
+
|
120
|
+
notify_observers(:action, tool_name, tool_input)
|
121
|
+
|
122
|
+
tool = tools.find{|t| t.name == tool_name}
|
123
|
+
|
124
|
+
tool.response(tool_input)
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_answer_from_response(response)
|
128
|
+
response.match(/Answer: ([\s\S]*)/)[1]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
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
@@ -48,7 +48,7 @@ module RubyBots
|
|
48
48
|
|
49
49
|
private
|
50
50
|
|
51
|
-
def run(
|
51
|
+
def run(input)
|
52
52
|
raise NotImplementedError
|
53
53
|
end
|
54
54
|
|
@@ -56,6 +56,8 @@ module RubyBots
|
|
56
56
|
JSON.parse(param)
|
57
57
|
rescue JSON::ParserError
|
58
58
|
errors << "invalid JSON"
|
59
|
+
rescue TypeError
|
60
|
+
errors << "value is not a String"
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
@@ -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
|
|
@@ -34,4 +35,5 @@ require "ruby_bots/bot"
|
|
34
35
|
require "ruby_bots/tools/openai_tool.rb"
|
35
36
|
require "ruby_bots/bots/openai_bot.rb"
|
36
37
|
require "ruby_bots/bots/pipeline_bot.rb"
|
37
|
-
require "ruby_bots/bots/router_bot.rb"
|
38
|
+
require "ruby_bots/bots/router_bot.rb"
|
39
|
+
require "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.22
|
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:
|