openai-toolable 0.1.1 → 0.1.7
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 +55 -13
- data/examples/simple.rb +7 -6
- data/lib/openai/toolable/tool_handler.rb +16 -4
- data/lib/openai/toolable/version.rb +1 -1
- data/lib/openai/toolable.rb +15 -13
- metadata +1 -2
- data/Gemfile.lock +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d3bbdddbe89db30b52f4fa021dd6b7ddbf38049e985d8a5ec57dd28bfed4f9d
|
4
|
+
data.tar.gz: cb046b9026c87e727b4bb6231eef4e288c574f625cf57ea9646839599e353772
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9ac230933b242f446fd2227b1746c86039e75cbc492b2812d89ee92c931c1b5f30daead64afb9f0c14f6411758a48bb11bb9255d871f8590c60dae384336767
|
7
|
+
data.tar.gz: 2d669d7c49966e3c2c0d5ec7744b83bd059d1e1a885a66e87e512ba2cf6391f9618e15747d3b2b4dd362f4a86e7ccff000210a76e6bdf3ed147293295c40ffa3
|
data/README.md
CHANGED
@@ -4,6 +4,14 @@
|
|
4
4
|
|
5
5
|
This gem is built on top of the `openai-ruby` gem and is designed to be a lightweight and intuitive extension for developers who want to leverage function calling in their Ruby applications.
|
6
6
|
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- **Easy tool definition**: Define tools with parameters, types, and descriptions
|
10
|
+
- **Automatic parameter validation**: Mark parameters as required to ensure they're included
|
11
|
+
- **Response handling**: Automatically parse and execute tool calls from OpenAI responses
|
12
|
+
- **Backward compatibility**: Works with both hash responses and OpenAI client response objects
|
13
|
+
- **Type safety**: Proper parameter type conversion and validation
|
14
|
+
|
7
15
|
## Installation
|
8
16
|
|
9
17
|
Add this line to your application's Gemfile:
|
@@ -26,14 +34,19 @@ Here's a simple example of how to use `openai-toolable` to define a tool and use
|
|
26
34
|
|
27
35
|
```ruby
|
28
36
|
require "openai/toolable"
|
37
|
+
require "openai"
|
38
|
+
|
39
|
+
# Set your API key
|
40
|
+
OPENAI_API_KEY = ENV["OPENAI_API_KEY"]
|
29
41
|
|
30
|
-
# Define a tool
|
42
|
+
# Define a tool with required parameters
|
31
43
|
weather_tool = Openai::Toolable::ToolFactory.build(
|
32
44
|
name: "get_weather",
|
45
|
+
type: "function",
|
33
46
|
description: "Get the current weather in a given location",
|
34
47
|
parameters: [
|
35
|
-
{ name: "location", type: :string, description: "The city and state, e.g. San Francisco, CA" },
|
36
|
-
{ name: "unit", type: :string, description: "The unit of temperature, e.g. celsius or fahrenheit" }
|
48
|
+
{ name: "location", type: :string, description: "The city and state, e.g. San Francisco, CA", required: true },
|
49
|
+
{ name: "unit", type: :string, description: "The unit of temperature, e.g. celsius or fahrenheit", required: true }
|
37
50
|
]
|
38
51
|
)
|
39
52
|
|
@@ -45,19 +58,48 @@ tool_handler.register(
|
|
45
58
|
)
|
46
59
|
|
47
60
|
# Create a client
|
48
|
-
client = OpenAI::Client.new
|
61
|
+
client = OpenAI::Client.new(api_key: OPENAI_API_KEY)
|
49
62
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
model: "gpt-
|
63
|
+
begin
|
64
|
+
# Create a chat completion
|
65
|
+
response = client.chat.completions.create(
|
66
|
+
model: "gpt-4o-mini",
|
54
67
|
messages: [{ role: "user", content: "What's the weather like in Boston?" }],
|
55
|
-
tools: [weather_tool.to_json]
|
56
|
-
|
57
|
-
)
|
68
|
+
tools: [weather_tool.to_json],
|
69
|
+
tool_choice: "auto"
|
70
|
+
)
|
71
|
+
|
72
|
+
# Handle the response
|
73
|
+
tool_handler.handle(response: response)
|
74
|
+
rescue StandardError => e
|
75
|
+
puts "An error occurred: #{e.message}"
|
76
|
+
end
|
77
|
+
```
|
58
78
|
|
59
|
-
|
60
|
-
|
79
|
+
## Parameter Configuration
|
80
|
+
|
81
|
+
Parameters can be configured with the following options:
|
82
|
+
|
83
|
+
- `name`: The parameter name (required)
|
84
|
+
- `type`: The parameter type (`:string`, `:number`, `:boolean`, etc.)
|
85
|
+
- `description`: A description of what the parameter is for
|
86
|
+
- `required`: Whether the parameter is required (default: `false`)
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
parameters: [
|
90
|
+
{
|
91
|
+
name: "location",
|
92
|
+
type: :string,
|
93
|
+
description: "The city and state, e.g. San Francisco, CA",
|
94
|
+
required: true
|
95
|
+
},
|
96
|
+
{
|
97
|
+
name: "unit",
|
98
|
+
type: :string,
|
99
|
+
description: "celsius or fahrenheit",
|
100
|
+
required: false
|
101
|
+
}
|
102
|
+
]
|
61
103
|
```
|
62
104
|
|
63
105
|
## Development
|
data/examples/simple.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "openai"
|
1
3
|
require "openai/toolable"
|
2
4
|
|
3
5
|
# Define a tool
|
4
6
|
weather_tool = Openai::Toolable::ToolFactory.build(
|
5
7
|
name: "get_weather",
|
6
|
-
type: "function",
|
7
8
|
description: "Get the current weather in a given location",
|
8
9
|
parameters: [
|
9
10
|
{ name: "location", type: :string, description: "The city and state, e.g. San Francisco, CA" },
|
@@ -15,17 +16,17 @@ weather_tool = Openai::Toolable::ToolFactory.build(
|
|
15
16
|
tool_handler = Openai::Toolable::ToolHandler.new
|
16
17
|
tool_handler.register(
|
17
18
|
name: "get_weather",
|
18
|
-
lambda: ->(location:, unit:) { puts "Getting the weather in #{location} (#{unit})..." }
|
19
|
+
lambda: ->(location:, unit: "celsius") { puts "Getting the weather in #{location} (#{unit})..." }
|
19
20
|
)
|
20
21
|
|
21
22
|
# Create a client
|
22
|
-
client = OpenAI::Client.new
|
23
|
+
client = OpenAI::Client.new
|
23
24
|
|
24
25
|
# Create a chat completion
|
25
26
|
response = client.chat.completions.create(
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
model: "gpt-3.5-turbo",
|
28
|
+
messages: [{ role: "user", content: "What's the weather like in Boston?" }],
|
29
|
+
tools: [weather_tool.to_json]
|
29
30
|
)
|
30
31
|
|
31
32
|
# Handle the response
|
@@ -12,11 +12,23 @@ module Openai
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def handle(response:)
|
15
|
-
|
16
|
-
|
15
|
+
# Handle both hash responses and OpenAI client response objects
|
16
|
+
if response.respond_to?(:choices)
|
17
|
+
# OpenAI client response object
|
18
|
+
tool_calls = response.choices[0]&.message&.tool_calls
|
19
|
+
return unless tool_calls && !tool_calls.empty?
|
20
|
+
|
21
|
+
tool_call = tool_calls[0]
|
22
|
+
function_name = tool_call.function.name
|
23
|
+
arguments = JSON.parse(tool_call.function.arguments)
|
24
|
+
else
|
25
|
+
# Hash response (backward compatibility)
|
26
|
+
tool_call = response.dig("choices", 0, "message", "tool_calls", 0)
|
27
|
+
return unless tool_call
|
17
28
|
|
18
|
-
|
19
|
-
|
29
|
+
function_name = tool_call.dig("function", "name")
|
30
|
+
arguments = JSON.parse(tool_call.dig("function", "arguments"))
|
31
|
+
end
|
20
32
|
|
21
33
|
@tools[function_name]&.call(**arguments.transform_keys(&:to_sym))
|
22
34
|
end
|
data/lib/openai/toolable.rb
CHANGED
@@ -7,9 +7,9 @@ module Openai
|
|
7
7
|
class Error < StandardError; end
|
8
8
|
|
9
9
|
class Tool
|
10
|
-
attr_reader :name, :
|
10
|
+
attr_reader :name, :description, :parameters, :type
|
11
11
|
|
12
|
-
def initialize(name:,
|
12
|
+
def initialize(name:, description:, parameters: [], type: "function")
|
13
13
|
@name = name
|
14
14
|
@type = type
|
15
15
|
@description = description
|
@@ -25,23 +25,25 @@ module Openai
|
|
25
25
|
|
26
26
|
def to_json
|
27
27
|
{
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
28
|
+
"type" => @type,
|
29
|
+
"function" => {
|
30
|
+
"name" => @name,
|
31
|
+
"description" => @description,
|
32
|
+
"parameters" => {
|
33
|
+
"type" => "object",
|
34
|
+
"properties" => @parameters.each_with_object({}) do |param, hash|
|
35
|
+
hash[param.name.to_s] = { "type" => param.type.to_s, "description" => param.description }
|
36
|
+
end,
|
37
|
+
"required" => @parameters.select(&:required).map { |p| p.name.to_s }
|
38
|
+
}
|
37
39
|
}
|
38
40
|
}
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
42
44
|
class ToolFactory
|
43
|
-
def self.build(name:,
|
44
|
-
Tool.new(name: name,
|
45
|
+
def self.build(name:, description:, parameters: [], type: "function")
|
46
|
+
Tool.new(name: name, description: description, parameters: parameters, type: type)
|
45
47
|
end
|
46
48
|
end
|
47
49
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openai-toolable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Russell Van Curen
|
@@ -33,7 +33,6 @@ extensions: []
|
|
33
33
|
extra_rdoc_files: []
|
34
34
|
files:
|
35
35
|
- Gemfile
|
36
|
-
- Gemfile.lock
|
37
36
|
- LICENSE.txt
|
38
37
|
- README.md
|
39
38
|
- Rakefile
|
data/Gemfile.lock
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
openai-toolable (0.1.1)
|
5
|
-
openai (~> 0.16.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
connection_pool (2.5.3)
|
11
|
-
minitest (5.25.5)
|
12
|
-
openai (0.16.0)
|
13
|
-
connection_pool
|
14
|
-
rake (13.3.0)
|
15
|
-
|
16
|
-
PLATFORMS
|
17
|
-
arm64-darwin-23
|
18
|
-
|
19
|
-
DEPENDENCIES
|
20
|
-
minitest (~> 5.0)
|
21
|
-
openai-toolable!
|
22
|
-
rake (~> 13.0)
|
23
|
-
|
24
|
-
BUNDLED WITH
|
25
|
-
2.4.1
|