tool_tailor 0.1.2 → 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 +2 -0
- data/Gemfile.lock +10 -1
- data/README.md +92 -88
- data/lib/tool_tailor/version.rb +1 -1
- data/lib/tool_tailor.rb +90 -83
- 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
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
tool_tailor (0.
|
4
|
+
tool_tailor (0.2.0)
|
5
5
|
yard (~> 0.9.36)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
+
attr_extras (7.1.0)
|
10
11
|
diff-lcs (1.5.1)
|
12
|
+
optimist (3.1.0)
|
13
|
+
patience_diff (1.2.0)
|
14
|
+
optimist (~> 3.0)
|
11
15
|
rake (12.3.3)
|
12
16
|
rspec (3.13.0)
|
13
17
|
rspec-core (~> 3.13.0)
|
@@ -22,6 +26,10 @@ GEM
|
|
22
26
|
diff-lcs (>= 1.2.0, < 2.0)
|
23
27
|
rspec-support (~> 3.13.0)
|
24
28
|
rspec-support (3.13.1)
|
29
|
+
super_diff (0.9.0)
|
30
|
+
attr_extras (>= 6.2.4)
|
31
|
+
diff-lcs
|
32
|
+
patience_diff
|
25
33
|
yard (0.9.36)
|
26
34
|
|
27
35
|
PLATFORMS
|
@@ -30,6 +38,7 @@ PLATFORMS
|
|
30
38
|
DEPENDENCIES
|
31
39
|
rake (~> 12.0)
|
32
40
|
rspec (~> 3.0)
|
41
|
+
super_diff (~> 0.9.0)
|
33
42
|
tool_tailor!
|
34
43
|
|
35
44
|
BUNDLED WITH
|
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,113 +20,115 @@ 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.
|
28
32
|
# @param unit [String] The unit of temperature, either 'celsius' or 'fahrenheit'.
|
29
|
-
def get_current_weather(location
|
33
|
+
def get_current_weather(location:, unit: 'celsius')
|
30
34
|
# Function implementation goes here
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
34
|
-
#
|
35
|
-
|
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
|
-
# "properties" => {
|
70
|
-
# "location" => {
|
71
|
-
# "type" => "string",
|
72
|
-
# "description" => "The city and state, e.g., San Francisco, CA."
|
73
|
-
# },
|
74
|
-
# "unit" => {
|
75
|
-
# "type" => "string",
|
76
|
-
# "description" => "The unit of temperature, either 'celsius' or 'fahrenheit'."
|
77
|
-
# },
|
78
|
-
# "api_key" => {
|
79
|
-
# "type" => "number",
|
80
|
-
# "description" => "The API key for the weather service."
|
81
|
-
# }
|
82
|
-
# },
|
83
|
-
# "required" => ["location", "unit", "api_key"]
|
84
|
-
# }
|
85
|
-
# }
|
86
|
-
# }
|
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))
|
87
73
|
```
|
88
74
|
|
89
|
-
|
90
|
-
|
91
|
-
```
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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"
|
100
89
|
},
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
],
|
105
|
-
tool_choice: {
|
106
|
-
type: "function",
|
107
|
-
function: {
|
108
|
-
name: "get_current_weather"
|
90
|
+
"age" => {
|
91
|
+
"type" => "integer",
|
92
|
+
"description" => "The user's age"
|
109
93
|
}
|
110
|
-
}
|
111
|
-
|
112
|
-
|
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
|
+
)
|
113
116
|
|
114
117
|
message = response.dig("choices", 0, "message")
|
115
118
|
|
116
119
|
if message["role"] == "assistant" && message["tool_calls"]
|
117
120
|
function_name = message.dig("tool_calls", 0, "function", "name")
|
118
|
-
args =
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
)
|
121
|
+
args = JSON.parse(
|
122
|
+
message.dig("tool_calls", 0, "function", "arguments"),
|
123
|
+
{ symbolize_names: true }
|
124
|
+
)
|
123
125
|
|
124
126
|
case function_name
|
125
|
-
when "
|
126
|
-
|
127
|
+
when "User"
|
128
|
+
user = User.new(**args)
|
129
|
+
puts "Created user: #{user.name}, age #{user.age}"
|
127
130
|
end
|
128
131
|
end
|
129
|
-
# => "The weather is nice 🌞"
|
130
132
|
```
|
131
133
|
|
132
134
|
## Development
|
@@ -137,10 +139,12 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
137
139
|
|
138
140
|
## Contributing
|
139
141
|
|
140
|
-
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).
|
141
143
|
|
142
144
|
## License
|
143
145
|
|
144
146
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
145
147
|
|
146
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,38 +5,83 @@ 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
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
+
#
|
14
|
+
# @example
|
15
|
+
# def example_method(param1:, param2:)
|
16
|
+
# # method implementation
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# ToolTailor.convert(method(:example_method))
|
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}"
|
16
37
|
end
|
38
|
+
end
|
17
39
|
|
18
|
-
|
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)
|
45
|
+
# Ensure only named arguments are allowed
|
46
|
+
unless method.parameters.all? { |type, _| type == :keyreq || type == :key }
|
47
|
+
raise ArgumentError, "Only named arguments are supported"
|
48
|
+
end
|
49
|
+
|
50
|
+
file_path, line_number = method.source_location
|
19
51
|
YARD.parse(file_path)
|
20
52
|
|
21
|
-
method_path = "#{
|
53
|
+
method_path = "#{method.owner}##{method.name}"
|
22
54
|
yard_object = YARD::Registry.at(method_path)
|
23
|
-
raise "Documentation for #{method_path} not found." if yard_object.nil?
|
24
|
-
|
25
|
-
function_description = yard_object.docstring
|
26
55
|
|
27
|
-
parameters
|
56
|
+
# Extract parameters from the method definition
|
57
|
+
parameters = method.parameters.map do |_, name|
|
28
58
|
{
|
29
|
-
name:
|
30
|
-
type:
|
31
|
-
description:
|
59
|
+
name: name.to_s,
|
60
|
+
type: "string",
|
61
|
+
description: ""
|
32
62
|
}
|
33
63
|
end
|
34
64
|
|
65
|
+
method_description = ""
|
66
|
+
|
67
|
+
if yard_object
|
68
|
+
method_description = yard_object.docstring
|
69
|
+
|
70
|
+
yard_object.tags("param").each do |tag|
|
71
|
+
param_name = tag.name.chomp(':')
|
72
|
+
param = parameters.find { |p| p[:name] == param_name }
|
73
|
+
if param
|
74
|
+
param[:type] = type_mapping(tag.types.first)
|
75
|
+
param[:description] = tag.text
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
35
80
|
{
|
36
81
|
type: "function",
|
37
82
|
function: {
|
38
|
-
name:
|
39
|
-
description:
|
83
|
+
name: method.name.to_s,
|
84
|
+
description: method_description,
|
40
85
|
parameters: {
|
41
86
|
type: "object",
|
42
87
|
properties: parameters.map do |param|
|
@@ -48,12 +93,29 @@ module ToolTailor
|
|
48
93
|
}
|
49
94
|
]
|
50
95
|
end.to_h,
|
51
|
-
required: parameters.
|
96
|
+
required: method.parameters.select { |type, _| type == :keyreq }.map { |_, name| name.to_s }
|
52
97
|
}
|
53
98
|
}
|
54
99
|
}.to_json
|
55
100
|
end
|
56
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
|
+
|
57
119
|
# Maps Ruby types to JSON schema types.
|
58
120
|
#
|
59
121
|
# @param type [String] The Ruby type to map.
|
@@ -77,38 +139,6 @@ end
|
|
77
139
|
class UnboundMethod
|
78
140
|
# Converts an UnboundMethod to a JSON schema.
|
79
141
|
#
|
80
|
-
# @example
|
81
|
-
# class ExampleClass
|
82
|
-
# # @param name [String] The name of the person.
|
83
|
-
# # @param age [Integer] The age of the person.
|
84
|
-
# def greet(name, age)
|
85
|
-
# puts "Hello, #{name}! You are #{age} years old."
|
86
|
-
# end
|
87
|
-
# end
|
88
|
-
#
|
89
|
-
# ExampleClass.instance_method(:greet).to_json_schema
|
90
|
-
# # => {
|
91
|
-
# # "type" => "function",
|
92
|
-
# # "function" => {
|
93
|
-
# # "name" => "greet",
|
94
|
-
# # "description" => "",
|
95
|
-
# # "parameters" => {
|
96
|
-
# # "type" => "object",
|
97
|
-
# # "properties" => {
|
98
|
-
# # "name" => {
|
99
|
-
# # "type" => "string",
|
100
|
-
# # "description" => "The name of the person."
|
101
|
-
# # },
|
102
|
-
# # "age" => {
|
103
|
-
# # "type" => "integer",
|
104
|
-
# # "description" => "The age of the person."
|
105
|
-
# # }
|
106
|
-
# # },
|
107
|
-
# # "required" => ["name", "age"]
|
108
|
-
# # }
|
109
|
-
# # }
|
110
|
-
# # }
|
111
|
-
#
|
112
142
|
# @return [String] The JSON schema representation of the method.
|
113
143
|
def to_json_schema
|
114
144
|
ToolTailor.convert(self)
|
@@ -118,40 +148,17 @@ end
|
|
118
148
|
class Method
|
119
149
|
# Converts a Method to a JSON schema.
|
120
150
|
#
|
121
|
-
# @example
|
122
|
-
# class ExampleClass
|
123
|
-
# # @param name [String] The name of the person.
|
124
|
-
# # @param age [Integer] The age of the person.
|
125
|
-
# def greet(name, age)
|
126
|
-
# puts "Hello, #{name}! You are #{age} years old."
|
127
|
-
# end
|
128
|
-
# end
|
129
|
-
#
|
130
|
-
# ExampleClass.new.method(:greet).to_json_schema
|
131
|
-
# # => {
|
132
|
-
# # "type" => "function",
|
133
|
-
# # "function" => {
|
134
|
-
# # "name" => "greet",
|
135
|
-
# # "description" => "",
|
136
|
-
# # "parameters" => {
|
137
|
-
# # "type" => "object",
|
138
|
-
# # "properties" => {
|
139
|
-
# # "name" => {
|
140
|
-
# # "type" => "string",
|
141
|
-
# # "description" => "The name of the person."
|
142
|
-
# # },
|
143
|
-
# # "age" => {
|
144
|
-
# # "type" => "integer",
|
145
|
-
# # "description" => "The age of the person."
|
146
|
-
# # }
|
147
|
-
# # },
|
148
|
-
# # "required" => ["name", "age"]
|
149
|
-
# # }
|
150
|
-
# # }
|
151
|
-
# # }
|
152
|
-
#
|
153
151
|
# @return [String] The JSON schema representation of the method.
|
154
152
|
def to_json_schema
|
155
153
|
ToolTailor.convert(self)
|
156
154
|
end
|
157
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
|