tool_tailor 0.1.3 → 0.2.0
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/Gemfile.lock +1 -1
- data/README.md +91 -90
- data/lib/tool_tailor/version.rb +1 -1
- data/lib/tool_tailor.rb +64 -82
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 840fcc0f9e8a7f5ccd4db0d8f4a8d88f2f344225d8f759442dc11f625d243300
|
4
|
+
data.tar.gz: 53289290ee809ea4e18acf08de8115d4c259b337823fd9ad506de6c335eb12db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 682eea6fb802c1a1444cafbe077577594cd682c7e02071313b56c7e737c738575b82098cbc6a4a63287edf449a0cd0aed9c4dcb225386350e4b2653270dac585
|
7
|
+
data.tar.gz: 8606a2abd5309a6ab9f7d7fdfa1910c953d674708aa9dce5c2e9fee29168f979632b3f4b8567dd2c5aba68d6fdfc18a2aebc000f464d63f20cc005c7c0452925
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# ToolTailor
|
2
2
|
|
3
|
-
ToolTailor is a Ruby gem that converts methods to OpenAI JSON schemas for use with tools, making it easier to integrate with OpenAI's API.
|
3
|
+
ToolTailor is a Ruby gem that converts methods and classes to OpenAI JSON schemas for use with tools, making it easier to integrate with OpenAI's API.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -20,8 +20,12 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
ToolTailor can convert both methods and classes to JSON schemas:
|
24
|
+
|
25
|
+
### Converting Methods
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
class WeatherService
|
25
29
|
# Get the current weather in a given location.
|
26
30
|
#
|
27
31
|
# @param location [String] The city and state, e.g., San Francisco, CA.
|
@@ -31,105 +35,100 @@ class TestClass
|
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
34
|
-
#
|
35
|
-
ToolTailor.convert(
|
36
|
-
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
#
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
#
|
68
|
-
|
69
|
-
# "description" => "Get the current weather in a given location.",
|
70
|
-
# "parameters" => {
|
71
|
-
# "type" => "object",
|
72
|
-
# "properties" => {
|
73
|
-
# "location" => {
|
74
|
-
# "type" => "string",
|
75
|
-
# "description" => "The city and state, e.g., San Francisco, CA."
|
76
|
-
# },
|
77
|
-
# "unit" => {
|
78
|
-
# "type" => "string",
|
79
|
-
# "description" => "The unit of temperature, either 'celsius' or 'fahrenheit'."
|
80
|
-
# },
|
81
|
-
# "api_key" => {
|
82
|
-
# "type" => "number",
|
83
|
-
# "description" => "The API key for the weather service."
|
84
|
-
# }
|
85
|
-
# },
|
86
|
-
# "required" => ["location", "unit", "api_key"]
|
87
|
-
# }
|
88
|
-
# }
|
89
|
-
# }
|
38
|
+
# Convert an instance method
|
39
|
+
schema = ToolTailor.convert(WeatherService.instance_method(:get_current_weather))
|
40
|
+
|
41
|
+
# Using to_json_schema on an unbound method
|
42
|
+
schema = WeatherService.instance_method(:get_current_weather).to_json_schema
|
43
|
+
|
44
|
+
# Using to_json_schema on a bound method
|
45
|
+
weather_service = WeatherService.new
|
46
|
+
schema = weather_service.method(:get_current_weather).to_json_schema
|
47
|
+
```
|
48
|
+
|
49
|
+
### Converting Classes
|
50
|
+
|
51
|
+
When passing a class, ToolTailor assumes you want to use the `new` method and generates the schema based on the `initialize` method:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
class User
|
55
|
+
# Create a new user
|
56
|
+
#
|
57
|
+
# @param name [String] The user's name
|
58
|
+
# @param age [Integer] The user's age
|
59
|
+
def initialize(name:, age:)
|
60
|
+
@name = name
|
61
|
+
@age = age
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Convert a class
|
66
|
+
schema = ToolTailor.convert(User)
|
67
|
+
|
68
|
+
# or
|
69
|
+
schema = User.to_json_schema
|
70
|
+
|
71
|
+
# This is equivalent to:
|
72
|
+
schema = ToolTailor.convert(User.instance_method(:initialize))
|
90
73
|
```
|
91
74
|
|
92
|
-
|
93
|
-
|
94
|
-
```
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
75
|
+
The resulting schema will look like this:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
{
|
79
|
+
"type" => "function",
|
80
|
+
"function" => {
|
81
|
+
"name" => "User",
|
82
|
+
"description" => "Create a new user",
|
83
|
+
"parameters" => {
|
84
|
+
"type" => "object",
|
85
|
+
"properties" => {
|
86
|
+
"name" => {
|
87
|
+
"type" => "string",
|
88
|
+
"description" => "The user's name"
|
103
89
|
},
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
],
|
108
|
-
tool_choice: {
|
109
|
-
type: "function",
|
110
|
-
function: {
|
111
|
-
name: "get_current_weather"
|
90
|
+
"age" => {
|
91
|
+
"type" => "integer",
|
92
|
+
"description" => "The user's age"
|
112
93
|
}
|
113
|
-
}
|
114
|
-
|
115
|
-
|
94
|
+
},
|
95
|
+
"required" => ["name", "age"]
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
```
|
100
|
+
|
101
|
+
### Using with ruby-openai
|
102
|
+
|
103
|
+
Here's an example of how to use ToolTailor with the [ruby-openai](https://github.com/alexrudall/ruby-openai) gem:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
response = client.chat(
|
107
|
+
parameters: {
|
108
|
+
model: "gpt-4",
|
109
|
+
messages: [
|
110
|
+
{ role: "user", content: "Create a user named Alice who is 30 years old" }
|
111
|
+
],
|
112
|
+
tools: [ToolTailor.convert(User)],
|
113
|
+
tool_choice: { type: "function", function: { name: "User" } }
|
114
|
+
}
|
115
|
+
)
|
116
116
|
|
117
117
|
message = response.dig("choices", 0, "message")
|
118
118
|
|
119
119
|
if message["role"] == "assistant" && message["tool_calls"]
|
120
120
|
function_name = message.dig("tool_calls", 0, "function", "name")
|
121
|
-
args =
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
)
|
121
|
+
args = JSON.parse(
|
122
|
+
message.dig("tool_calls", 0, "function", "arguments"),
|
123
|
+
{ symbolize_names: true }
|
124
|
+
)
|
126
125
|
|
127
126
|
case function_name
|
128
|
-
when "
|
129
|
-
|
127
|
+
when "User"
|
128
|
+
user = User.new(**args)
|
129
|
+
puts "Created user: #{user.name}, age #{user.age}"
|
130
130
|
end
|
131
131
|
end
|
132
|
-
# => "The weather is nice 🌞"
|
133
132
|
```
|
134
133
|
|
135
134
|
## Development
|
@@ -140,10 +139,12 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
140
139
|
|
141
140
|
## Contributing
|
142
141
|
|
143
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
142
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/kieranklaassen/tool_tailor. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/kieranklaassen/tool_tailor/blob/master/CODE_OF_CONDUCT.md).
|
144
143
|
|
145
144
|
## License
|
146
145
|
|
147
146
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
148
147
|
|
149
148
|
## Code of Conduct
|
149
|
+
|
150
|
+
Everyone interacting in the ToolTailor project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/kieranklaassen/tool_tailor/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/tool_tailor/version.rb
CHANGED
data/lib/tool_tailor.rb
CHANGED
@@ -5,36 +5,56 @@ require "yard"
|
|
5
5
|
module ToolTailor
|
6
6
|
class Error < StandardError; end
|
7
7
|
|
8
|
-
# Converts a function to a JSON schema representation.
|
8
|
+
# Converts a function or class to a JSON schema representation.
|
9
9
|
#
|
10
|
-
# @param
|
11
|
-
# @return [String] The JSON schema representation of the function.
|
12
|
-
# @raise [ArgumentError] If the provided object is not a Method or
|
10
|
+
# @param object [Method, UnboundMethod, Class] The function or class to convert.
|
11
|
+
# @return [String] The JSON schema representation of the function or class.
|
12
|
+
# @raise [ArgumentError] If the provided object is not a Method, UnboundMethod, or Class.
|
13
13
|
#
|
14
14
|
# @example
|
15
|
-
# def example_method(param1
|
15
|
+
# def example_method(param1:, param2:)
|
16
16
|
# # method implementation
|
17
17
|
# end
|
18
18
|
#
|
19
19
|
# ToolTailor.convert(method(:example_method))
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# class ExampleClass
|
23
|
+
# def initialize(param1:, param2:)
|
24
|
+
# # initialization
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# ToolTailor.convert(ExampleClass)
|
29
|
+
def self.convert(object)
|
30
|
+
case object
|
31
|
+
when Method, UnboundMethod
|
32
|
+
convert_method(object)
|
33
|
+
when Class
|
34
|
+
convert_class(object)
|
35
|
+
else
|
36
|
+
raise ArgumentError, "Unsupported object type: #{object.class}"
|
23
37
|
end
|
38
|
+
end
|
24
39
|
|
40
|
+
# Converts a method to a JSON schema representation.
|
41
|
+
#
|
42
|
+
# @param method [Method, UnboundMethod] The method to convert.
|
43
|
+
# @return [String] The JSON schema representation of the method.
|
44
|
+
def self.convert_method(method)
|
25
45
|
# Ensure only named arguments are allowed
|
26
|
-
unless
|
46
|
+
unless method.parameters.all? { |type, _| type == :keyreq || type == :key }
|
27
47
|
raise ArgumentError, "Only named arguments are supported"
|
28
48
|
end
|
29
49
|
|
30
|
-
file_path, line_number =
|
50
|
+
file_path, line_number = method.source_location
|
31
51
|
YARD.parse(file_path)
|
32
52
|
|
33
|
-
method_path = "#{
|
53
|
+
method_path = "#{method.owner}##{method.name}"
|
34
54
|
yard_object = YARD::Registry.at(method_path)
|
35
55
|
|
36
|
-
# Extract parameters from the
|
37
|
-
parameters =
|
56
|
+
# Extract parameters from the method definition
|
57
|
+
parameters = method.parameters.map do |_, name|
|
38
58
|
{
|
39
59
|
name: name.to_s,
|
40
60
|
type: "string",
|
@@ -42,10 +62,10 @@ module ToolTailor
|
|
42
62
|
}
|
43
63
|
end
|
44
64
|
|
45
|
-
|
65
|
+
method_description = ""
|
46
66
|
|
47
67
|
if yard_object
|
48
|
-
|
68
|
+
method_description = yard_object.docstring
|
49
69
|
|
50
70
|
yard_object.tags("param").each do |tag|
|
51
71
|
param_name = tag.name.chomp(':')
|
@@ -60,8 +80,8 @@ module ToolTailor
|
|
60
80
|
{
|
61
81
|
type: "function",
|
62
82
|
function: {
|
63
|
-
name:
|
64
|
-
description:
|
83
|
+
name: method.name.to_s,
|
84
|
+
description: method_description,
|
65
85
|
parameters: {
|
66
86
|
type: "object",
|
67
87
|
properties: parameters.map do |param|
|
@@ -73,12 +93,29 @@ module ToolTailor
|
|
73
93
|
}
|
74
94
|
]
|
75
95
|
end.to_h,
|
76
|
-
required:
|
96
|
+
required: method.parameters.select { |type, _| type == :keyreq }.map { |_, name| name.to_s }
|
77
97
|
}
|
78
98
|
}
|
79
99
|
}.to_json
|
80
100
|
end
|
81
101
|
|
102
|
+
def self.convert_class(klass)
|
103
|
+
initialize_method = klass.instance_method(:initialize)
|
104
|
+
schema = JSON.parse(convert_method(initialize_method))
|
105
|
+
schema["function"]["name"] = klass.name
|
106
|
+
|
107
|
+
# Re-parse YARD documentation for the class
|
108
|
+
file_path, _ = initialize_method.source_location
|
109
|
+
YARD.parse(file_path)
|
110
|
+
class_object = YARD::Registry.at(klass.name)
|
111
|
+
|
112
|
+
if class_object
|
113
|
+
schema["function"]["description"] = class_object.docstring.to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
schema.to_json
|
117
|
+
end
|
118
|
+
|
82
119
|
# Maps Ruby types to JSON schema types.
|
83
120
|
#
|
84
121
|
# @param type [String] The Ruby type to map.
|
@@ -102,38 +139,6 @@ end
|
|
102
139
|
class UnboundMethod
|
103
140
|
# Converts an UnboundMethod to a JSON schema.
|
104
141
|
#
|
105
|
-
# @example
|
106
|
-
# class ExampleClass
|
107
|
-
# # @param name [String] The name of the person.
|
108
|
-
# # @param age [Integer] The age of the person.
|
109
|
-
# def greet(name, age)
|
110
|
-
# puts "Hello, #{name}! You are #{age} years old."
|
111
|
-
# end
|
112
|
-
# end
|
113
|
-
#
|
114
|
-
# ExampleClass.instance_method(:greet).to_json_schema
|
115
|
-
# # => {
|
116
|
-
# # "type" => "function",
|
117
|
-
# # "function" => {
|
118
|
-
# # "name" => "greet",
|
119
|
-
# # "description" => "",
|
120
|
-
# # "parameters" => {
|
121
|
-
# # "type" => "object",
|
122
|
-
# # "properties" => {
|
123
|
-
# # "name" => {
|
124
|
-
# # "type" => "string",
|
125
|
-
# # "description" => "The name of the person."
|
126
|
-
# # },
|
127
|
-
# # "age" => {
|
128
|
-
# # "type" => "integer",
|
129
|
-
# # "description" => "The age of the person."
|
130
|
-
# # }
|
131
|
-
# # },
|
132
|
-
# # "required" => ["name", "age"]
|
133
|
-
# # }
|
134
|
-
# # }
|
135
|
-
# # }
|
136
|
-
#
|
137
142
|
# @return [String] The JSON schema representation of the method.
|
138
143
|
def to_json_schema
|
139
144
|
ToolTailor.convert(self)
|
@@ -143,40 +148,17 @@ end
|
|
143
148
|
class Method
|
144
149
|
# Converts a Method to a JSON schema.
|
145
150
|
#
|
146
|
-
# @example
|
147
|
-
# class ExampleClass
|
148
|
-
# # @param name [String] The name of the person.
|
149
|
-
# # @param age [Integer] The age of the person.
|
150
|
-
# def greet(name, age)
|
151
|
-
# puts "Hello, #{name}! You are #{age} years old."
|
152
|
-
# end
|
153
|
-
# end
|
154
|
-
#
|
155
|
-
# ExampleClass.new.method(:greet).to_json_schema
|
156
|
-
# # => {
|
157
|
-
# # "type" => "function",
|
158
|
-
# # "function" => {
|
159
|
-
# # "name" => "greet",
|
160
|
-
# # "description" => "",
|
161
|
-
# # "parameters" => {
|
162
|
-
# # "type" => "object",
|
163
|
-
# # "properties" => {
|
164
|
-
# # "name" => {
|
165
|
-
# # "type" => "string",
|
166
|
-
# # "description" => "The name of the person."
|
167
|
-
# # },
|
168
|
-
# # "age" => {
|
169
|
-
# # "type" => "integer",
|
170
|
-
# # "description" => "The age of the person."
|
171
|
-
# # }
|
172
|
-
# # },
|
173
|
-
# # "required" => ["name", "age"]
|
174
|
-
# # }
|
175
|
-
# # }
|
176
|
-
# # }
|
177
|
-
#
|
178
151
|
# @return [String] The JSON schema representation of the method.
|
179
152
|
def to_json_schema
|
180
153
|
ToolTailor.convert(self)
|
181
154
|
end
|
182
155
|
end
|
156
|
+
|
157
|
+
class Class
|
158
|
+
# Converts a Class to a JSON schema.
|
159
|
+
#
|
160
|
+
# @return [String] The JSON schema representation of the class's initialize method.
|
161
|
+
def to_json_schema
|
162
|
+
ToolTailor.convert(self)
|
163
|
+
end
|
164
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tool_tailor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kieran Klaassen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yard
|