func_bot 0.1.0 → 0.1.1
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/README.md +10 -3
- data/lib/func_bot/bots/client.rb +12 -4
- data/lib/func_bot/bots/history.rb +9 -4
- data/lib/func_bot/bots/message.rb +5 -5
- data/lib/func_bot/functions/base_function.rb +3 -1
- data/lib/func_bot/functions/handler.rb +36 -0
- data/lib/func_bot/functions/list.rb +3 -3
- data/lib/func_bot/version.rb +1 -1
- data/lib/func_bot.rb +28 -3
- data/lib/generators/func_bot/function_generator.rb +16 -14
- data/lib/generators/func_bot/install_generator.rb +2 -5
- metadata +10 -7
- data/lib/func_bot/bot.rb +0 -41
- data/lib/func_bot/handlers/bot_handler.rb +0 -20
- data/lib/func_bot/handlers/function_handler.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68e7cc778676e7b037cff5572cd69c2f61559763fd57b8e31328f487fa839148
|
4
|
+
data.tar.gz: cb9ca4334b4c08384216a03988e7b3343809cf7d0ee9e0ba69706b17a3c91e48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b22ff1a5ebedc9261dc022bed342d8272b1bd15ed73c749038c4c2711f7785fa9dae7452fe0e8a40337dc46ae43daa2df7bc7ba7b4af28a8fa3f0844aff9efe3
|
7
|
+
data.tar.gz: 45ac6d18634f9615dcbd3807726543bab58409a7c6e20d80c8a0ad039a5f9bbcccf1c2e801c6b1070bce3aaf4632d435538aa6313f468e22dfe889efca24c156
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# FuncBot
|
2
2
|
|
3
|
-
FuncBot is a Rails gem built on top of the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem
|
3
|
+
FuncBot is a Rails gem built on top of the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem. It allows you to easily create chatbots that can answer questions by calling on functions you define. It's goal is to provide a simple interface to consume [OpenAI's Function Calling API](https://openai.com/blog/function-calling-and-other-api-updates?ref=upstract.com).
|
4
4
|
|
5
5
|
## Usage
|
6
6
|
|
@@ -10,7 +10,7 @@ FuncBot is a Rails gem built on top of the [ruby-openai](https://github.com/alex
|
|
10
10
|
rails g func_bot:function <function_name>
|
11
11
|
```
|
12
12
|
|
13
|
-
- Update the function in `lib/func_bot/functions/<function_name>.rb`
|
13
|
+
- Update the function in `app/lib/func_bot/functions/<function_name>.rb`
|
14
14
|
|
15
15
|
- A function can be as simple or as complex as you need it to be. Your bot will process the results and express them to the user.
|
16
16
|
- All functions must have an `execute` method.
|
@@ -38,7 +38,7 @@ rails g func_bot:function <function_name>
|
|
38
38
|
- `parsed_response` is a hash that contains the response relevant to your function from OpenAI.
|
39
39
|
- `response` is the raw response from OpenAI.
|
40
40
|
|
41
|
-
- Update your new function in the list of functions in `lib/func_bot/functions/list.yml`.
|
41
|
+
- Update your new function in the list of functions in `app/lib/func_bot/functions/list.yml`.
|
42
42
|
- This list of functions will be available to the bot with every request.
|
43
43
|
- Adding good descriptions to the functions will help the bot infer when to use which function.
|
44
44
|
- If the user asks a question that is not related to a function in your list, the bot will just ask ChatGPT.
|
@@ -80,6 +80,13 @@ rails g func_bot:install
|
|
80
80
|
|
81
81
|
```
|
82
82
|
|
83
|
+
- Make sure to add your OpenAI API key to your credentials file or update the `config/initializers/openai.rb` file accordingly.
|
84
|
+
|
85
|
+
```yml
|
86
|
+
openai:
|
87
|
+
api_key: your-private-key
|
88
|
+
```
|
89
|
+
|
83
90
|
## Testing
|
84
91
|
|
85
92
|
```bash
|
data/lib/func_bot/bots/client.rb
CHANGED
@@ -3,18 +3,26 @@
|
|
3
3
|
module FuncBot
|
4
4
|
module Bots
|
5
5
|
class Client
|
6
|
-
|
7
|
-
|
6
|
+
attr_accessor :bot
|
7
|
+
|
8
|
+
def initialize(bot)
|
9
|
+
@bot = bot
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
open_ai.chat(
|
8
14
|
parameters: {
|
9
15
|
model: "gpt-3.5-turbo-0613",
|
10
|
-
messages:
|
16
|
+
messages: bot.history.payload,
|
11
17
|
temperature: 0.7,
|
12
18
|
functions: FuncBot::Functions::List.call
|
13
19
|
}
|
14
20
|
)
|
15
21
|
end
|
16
22
|
|
17
|
-
|
23
|
+
private
|
24
|
+
|
25
|
+
def open_ai
|
18
26
|
@client ||= OpenAI::Client.new
|
19
27
|
end
|
20
28
|
end
|
@@ -1,13 +1,18 @@
|
|
1
1
|
module FuncBot
|
2
2
|
module Bots
|
3
3
|
class History
|
4
|
-
attr_accessor :
|
4
|
+
attr_accessor :messages
|
5
|
+
|
5
6
|
def initialize
|
6
|
-
@
|
7
|
+
@messages = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def chronicle(role, prompt, name = nil)
|
11
|
+
messages << Message.new(role, prompt, name)
|
7
12
|
end
|
8
13
|
|
9
|
-
def
|
10
|
-
|
14
|
+
def payload
|
15
|
+
messages.map(&:data)
|
11
16
|
end
|
12
17
|
end
|
13
18
|
end
|
@@ -1,19 +1,19 @@
|
|
1
1
|
module FuncBot
|
2
2
|
module Bots
|
3
3
|
class Message
|
4
|
-
attr_accessor :role, :
|
4
|
+
attr_accessor :role, :content, :name
|
5
5
|
|
6
|
-
def initialize(role,
|
6
|
+
def initialize(role, content, name = nil)
|
7
7
|
@role = role
|
8
|
-
@
|
8
|
+
@content = content
|
9
9
|
@name = name
|
10
10
|
end
|
11
11
|
|
12
12
|
def data
|
13
13
|
if name.nil?
|
14
|
-
{role: role, content:
|
14
|
+
{role: role, content: content}
|
15
15
|
else
|
16
|
-
{role: role, content:
|
16
|
+
{role: role, content: content, name: name}
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FuncBot
|
4
|
+
module Functions
|
5
|
+
class Handler
|
6
|
+
attr_accessor :prompt, :bot
|
7
|
+
|
8
|
+
def initialize(bot)
|
9
|
+
@bot = bot
|
10
|
+
end
|
11
|
+
|
12
|
+
def handle
|
13
|
+
bot.history.chronicle("function", function_data, function_name)
|
14
|
+
bot.client.call
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def function_data
|
20
|
+
constantize_function.new(bot.response).execute
|
21
|
+
end
|
22
|
+
|
23
|
+
def constantize_function
|
24
|
+
"FuncBot::Functions::#{function_name}".constantize
|
25
|
+
end
|
26
|
+
|
27
|
+
def function_name
|
28
|
+
bot.response.dig("choices", 0, "message", "function_call", "name")
|
29
|
+
end
|
30
|
+
|
31
|
+
def dig_for_content
|
32
|
+
bot.response.dig("choices", 0, "message", "content")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -4,10 +4,10 @@ module FuncBot
|
|
4
4
|
module Functions
|
5
5
|
class List
|
6
6
|
def self.call
|
7
|
-
if File.exist?(Rails.root.join("lib", "func_bot", "functions", "list.yml"))
|
8
|
-
YAML.load_file(Rails.root.join("lib", "func_bot", "functions", "list.yml"))["functions"]
|
7
|
+
if File.exist?(Rails.root.join("app", "lib", "func_bot", "functions", "list.yml"))
|
8
|
+
YAML.load_file(Rails.root.join("app", "lib", "func_bot", "functions", "list.yml"))["functions"]
|
9
9
|
else
|
10
|
-
raise "lib/func_bot/functions/list.yml file not found. Please create it by running rails func_bot:install."
|
10
|
+
raise "app/lib/func_bot/functions/list.yml file not found. Please create it by running rails func_bot:install."
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/lib/func_bot/version.rb
CHANGED
data/lib/func_bot.rb
CHANGED
@@ -3,14 +3,39 @@ require "openai"
|
|
3
3
|
require "func_bot/version"
|
4
4
|
require "func_bot/engine"
|
5
5
|
|
6
|
-
require_relative "func_bot/bot"
|
7
6
|
require_relative "func_bot/bots/client"
|
8
7
|
require_relative "func_bot/bots/history"
|
9
8
|
require_relative "func_bot/bots/message"
|
10
9
|
require_relative "func_bot/functions/base_function"
|
10
|
+
require_relative "func_bot/functions/handler"
|
11
11
|
require_relative "func_bot/functions/list"
|
12
|
-
require_relative "func_bot/handlers/bot_handler"
|
13
|
-
require_relative "func_bot/handlers/function_handler"
|
14
12
|
|
15
13
|
module FuncBot
|
14
|
+
class Bot
|
15
|
+
attr_accessor :response
|
16
|
+
attr_reader :client, :history
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@history = Bots::History.new
|
20
|
+
@client = Bots::Client.new(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def ask(prompt)
|
24
|
+
history.chronicle("user", prompt)
|
25
|
+
self.response = client.call
|
26
|
+
self.response = Functions::Handler.new(self).handle if available_function?
|
27
|
+
history.chronicle("assistant", content)
|
28
|
+
history.messages.last.content
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def available_function?
|
34
|
+
response.dig("choices", 0, "message", "function_call").present?
|
35
|
+
end
|
36
|
+
|
37
|
+
def content
|
38
|
+
response.dig("choices", 0, "message", "content")
|
39
|
+
end
|
40
|
+
end
|
16
41
|
end
|
@@ -3,7 +3,7 @@ module FuncBot
|
|
3
3
|
source_root File.expand_path("templates", __dir__)
|
4
4
|
|
5
5
|
def generate_function
|
6
|
-
template "function.rb", "lib/func_bot/functions/#{file_name}_function.rb"
|
6
|
+
template "function.rb", "app/lib/func_bot/functions/#{file_name}_function.rb"
|
7
7
|
end
|
8
8
|
|
9
9
|
def append_to_functions_list
|
@@ -15,22 +15,24 @@ module FuncBot
|
|
15
15
|
private
|
16
16
|
|
17
17
|
def yml_file
|
18
|
-
Rails.root.join("lib", "func_bot", "functions", "list.yml")
|
18
|
+
Rails.root.join("app", "lib", "func_bot", "functions", "list.yml")
|
19
19
|
end
|
20
20
|
|
21
21
|
def function_template
|
22
|
-
{
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
22
|
+
{
|
23
|
+
name: "#{class_name}Function",
|
24
|
+
description: "TODO: Write a description for this function.",
|
25
|
+
parameters: {
|
26
|
+
type: "TODO: choose from string, integer, boolean, object, etc.",
|
27
|
+
properties: {
|
28
|
+
location: {
|
29
|
+
type: "TODO: Write a type for this parameter. e.g. string, integer, etc.",
|
30
|
+
description: "TODO: Write a description for this parameter."
|
31
|
+
}
|
32
|
+
},
|
33
|
+
required: ["TODO: list the required parameters here"]
|
34
|
+
}
|
35
|
+
}
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
@@ -4,11 +4,8 @@ module FuncBot
|
|
4
4
|
|
5
5
|
def move_files_to_lib
|
6
6
|
copy_file "openai.rb", "config/initializers/openai.rb"
|
7
|
-
copy_file "list.yml", "lib/func_bot/functions/list.yml"
|
8
|
-
copy_file "weather_function.rb", "lib/func_bot/functions/weather_function.rb"
|
9
|
-
application do
|
10
|
-
"config.autoload_paths << Rails.root.join('lib')"
|
11
|
-
end
|
7
|
+
copy_file "list.yml", "app/lib/func_bot/functions/list.yml"
|
8
|
+
copy_file "weather_function.rb", "app/lib/func_bot/functions/weather_function.rb"
|
12
9
|
end
|
13
10
|
end
|
14
11
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: func_bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- lbp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -192,7 +192,10 @@ dependencies:
|
|
192
192
|
- - ">="
|
193
193
|
- !ruby/object:Gem::Version
|
194
194
|
version: '0'
|
195
|
-
description:
|
195
|
+
description: FuncBot is a Rails gem built on top of the ruby-openai gem. It helps
|
196
|
+
you create chatbots that can answer questions by calling on functions you define.
|
197
|
+
It's goal is to provide a simple interface to consume OpenAI's Function Calling
|
198
|
+
API.
|
196
199
|
email:
|
197
200
|
- 43428385+leopolicastro@users.noreply.github.com
|
198
201
|
executables: []
|
@@ -213,15 +216,13 @@ files:
|
|
213
216
|
- config/initializers/openai.rb
|
214
217
|
- config/routes.rb
|
215
218
|
- lib/func_bot.rb
|
216
|
-
- lib/func_bot/bot.rb
|
217
219
|
- lib/func_bot/bots/client.rb
|
218
220
|
- lib/func_bot/bots/history.rb
|
219
221
|
- lib/func_bot/bots/message.rb
|
220
222
|
- lib/func_bot/engine.rb
|
221
223
|
- lib/func_bot/functions/base_function.rb
|
224
|
+
- lib/func_bot/functions/handler.rb
|
222
225
|
- lib/func_bot/functions/list.rb
|
223
|
-
- lib/func_bot/handlers/bot_handler.rb
|
224
|
-
- lib/func_bot/handlers/function_handler.rb
|
225
226
|
- lib/func_bot/version.rb
|
226
227
|
- lib/generators/func_bot/function_generator.rb
|
227
228
|
- lib/generators/func_bot/install_generator.rb
|
@@ -255,5 +256,7 @@ requirements: []
|
|
255
256
|
rubygems_version: 3.4.13
|
256
257
|
signing_key:
|
257
258
|
specification_version: 4
|
258
|
-
summary:
|
259
|
+
summary: FuncBot is a Rails gem built on top of the ruby-openai gem. It helps you
|
260
|
+
create chatbots that can answer questions by calling on functions you define. It's
|
261
|
+
goal is to provide a simple interface to consume OpenAI's Function Calling API.
|
259
262
|
test_files: []
|
data/lib/func_bot/bot.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FuncBot
|
4
|
-
class Bot
|
5
|
-
attr_accessor :role, :prompt
|
6
|
-
|
7
|
-
delegate :history, to: :@history
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
@history = Bots::History.new
|
11
|
-
end
|
12
|
-
|
13
|
-
def ask(prompt)
|
14
|
-
@prompt = prompt
|
15
|
-
@role = "user"
|
16
|
-
handle_response(call_openai)
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def call_openai
|
22
|
-
Bots::Client.call(chat_history)
|
23
|
-
end
|
24
|
-
|
25
|
-
def handle_response(response)
|
26
|
-
if function_call?(response)
|
27
|
-
Handlers::FunctionHandler.call(response, history)
|
28
|
-
else
|
29
|
-
Handlers::BotHandler.call(response, history)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def function_call?(response)
|
34
|
-
response.dig("choices", 0, "message", "function_call").present?
|
35
|
-
end
|
36
|
-
|
37
|
-
def chat_history
|
38
|
-
@history.push_prompt(role, prompt)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FuncBot
|
4
|
-
module Handlers
|
5
|
-
class BotHandler
|
6
|
-
class << self
|
7
|
-
def call(response, history)
|
8
|
-
history << Bots::Message.new("assistant", dig_for_content(response)).data
|
9
|
-
dig_for_content(response)
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def dig_for_content(response)
|
15
|
-
response.dig("choices", 0, "message", "content")
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module FuncBot
|
4
|
-
module Handlers
|
5
|
-
class FunctionHandler
|
6
|
-
attr_accessor :prompt, :history
|
7
|
-
|
8
|
-
class << self
|
9
|
-
def call(response, history)
|
10
|
-
@function_name = nil
|
11
|
-
@history = history
|
12
|
-
function_return = constantize_function(response).new(response).execute
|
13
|
-
response = respond_to(function_return)
|
14
|
-
history << Bots::Message.new("assistant", dig_for_content(response)).data
|
15
|
-
dig_for_content(response)
|
16
|
-
end
|
17
|
-
|
18
|
-
def constantize_function(response)
|
19
|
-
"FuncBot::Functions::#{function_name(response)}".constantize
|
20
|
-
end
|
21
|
-
|
22
|
-
def function_name(response = {})
|
23
|
-
@function_name ||= response.dig("choices", 0, "message", "function_call", "name")
|
24
|
-
end
|
25
|
-
|
26
|
-
def dig_for_content(response)
|
27
|
-
response.dig("choices", 0, "message", "content")
|
28
|
-
end
|
29
|
-
|
30
|
-
def respond_to(prompt)
|
31
|
-
@prompt = prompt
|
32
|
-
Bots::Client.call(messages)
|
33
|
-
end
|
34
|
-
|
35
|
-
def messages
|
36
|
-
@history << Bots::Message.new("function", @prompt, function_name).data
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|