tool_tailor 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b242798d32a1e165bcfee93e5562b97015ad554acdd6837e3ee729b3b27e6ed
4
- data.tar.gz: 3a9a097551149899b80e71b00dc936c6942dd213d66d97fb040485aeadd1408d
3
+ metadata.gz: abc31d6d69830be0126f8b6f61aee408be9e17bdb91423b68e9166f30efaf21f
4
+ data.tar.gz: 7eb42498a63836eeac46c6996d2e32f34303430b58dcdd36cb89d833cbadeaef
5
5
  SHA512:
6
- metadata.gz: 342c3b07cb9ee1b01ead72205ce15b7b1cde92f10b3ba3d16cdfc18114d76c04b6c4bdd64a3a8c10fa0c1c3927248d4b3d7e8586edaca1c0d7e13ac1f49b018e
7
- data.tar.gz: 347ff4fe7ab0d29164d48e97a38400e4d8bb7816c81b791baab903ebdc764f8b4fcf97156dc256e56d66d6eddca92185c80740c488889342ffb9efaeabbd8aec
6
+ metadata.gz: 04d10928c7453fbd20980d3c23a6bbbe8cf000baa25f2ffce903910b21607088762f300fde528ee4c7f0b8673ba58a65224926c261486ff033d87c5d25601468
7
+ data.tar.gz: 779238cd2409d9d6c669cab52f0d286fc79cf3c9335082d88bf62d871f23ec6439fe479f465d25fab0bd13d2285e6522ee026ea9e5257b019cda483244c28664
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tool_tailor (0.1.1)
4
+ tool_tailor (0.1.2)
5
5
  yard (~> 0.9.36)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -26,12 +26,12 @@ class TestClass
26
26
  #
27
27
  # @param location [String] The city and state, e.g., San Francisco, CA.
28
28
  # @param unit [String] The unit of temperature, either 'celsius' or 'fahrenheit'.
29
- # @param api_key [Float] The API key for the weather service.
30
- def get_current_weather(location, unit = 'celsius', api_key: nil)
29
+ def get_current_weather(location, unit = 'celsius')
31
30
  # Function implementation goes here
32
31
  end
33
32
  end
34
33
 
34
+ # Unbound method
35
35
  TestClass.instance_method(:get_current_weather).to_json_schema # => {
36
36
  # "type" => "function",
37
37
  # "function" => {
@@ -56,6 +56,77 @@ TestClass.instance_method(:get_current_weather).to_json_schema # => {
56
56
  # "required" => ["location", "unit", "api_key"]
57
57
  # }
58
58
  # }
59
+
60
+ # Bound method
61
+ example_instance = TestClass.new
62
+ example_instance.method(:get_current_weather).to_json_schema # => {
63
+ # "type" => "function",
64
+ # "function" => {
65
+ # "name" => "get_current_weather",
66
+ # "description" => "Get the current weather in a given location.",
67
+ # "parameters" => {
68
+ # "type" => "object",
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
+ # }
87
+ ```
88
+
89
+ And with [ruby-openai](https://github.com/alexrudall/ruby-openai):
90
+
91
+ ```rb
92
+ response =
93
+ client.chat(
94
+ parameters: {
95
+ model: "gpt-4o",
96
+ messages: [
97
+ {
98
+ "role": "user",
99
+ "content": "What is the weather like in San Francisco?",
100
+ },
101
+ ],
102
+ tools: [
103
+ TestClass.instance_method(:get_current_weather).to_json_schema
104
+ ],
105
+ tool_choice: {
106
+ type: "function",
107
+ function: {
108
+ name: "get_current_weather"
109
+ }
110
+ }
111
+ },
112
+ )
113
+
114
+ message = response.dig("choices", 0, "message")
115
+
116
+ if message["role"] == "assistant" && message["tool_calls"]
117
+ function_name = message.dig("tool_calls", 0, "function", "name")
118
+ args =
119
+ JSON.parse(
120
+ message.dig("tool_calls", 0, "function", "arguments"),
121
+ { symbolize_names: true },
122
+ )
123
+
124
+ case function_name
125
+ when "get_current_weather"
126
+ TestClass.get_current_weather(**args)
127
+ end
128
+ end
129
+ # => "The weather is nice 🌞"
59
130
  ```
60
131
 
61
132
  ## Development
@@ -1,3 +1,3 @@
1
1
  module ToolTailor
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
data/lib/tool_tailor.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  require "tool_tailor/version"
2
- require 'json'
3
- require 'yard'
2
+ require "json"
3
+ require "yard"
4
4
 
5
5
  module ToolTailor
6
6
  class Error < StandardError; end
7
7
 
8
8
  # Converts a function to a JSON schema representation.
9
9
  #
10
- # @param function [Method] The function to convert.
10
+ # @param function [Method, UnboundMethod] The function to convert.
11
11
  # @return [String] The JSON schema representation of the function.
12
12
  # @raise [ArgumentError] If the provided object is not a Method or UnboundMethod.
13
13
  def self.convert(function)
@@ -18,14 +18,13 @@ module ToolTailor
18
18
  file_path, line_number = function.source_location
19
19
  YARD.parse(file_path)
20
20
 
21
- # Construct the correct identifier for the YARD object
22
21
  method_path = "#{function.owner}##{function.name}"
23
22
  yard_object = YARD::Registry.at(method_path)
24
23
  raise "Documentation for #{method_path} not found." if yard_object.nil?
25
24
 
26
25
  function_description = yard_object.docstring
27
26
 
28
- parameters = yard_object.tags('param').map do |tag|
27
+ parameters = yard_object.tags("param").map do |tag|
29
28
  {
30
29
  name: tag.name,
31
30
  type: type_mapping(tag.types.first),
@@ -57,33 +56,101 @@ module ToolTailor
57
56
 
58
57
  # Maps Ruby types to JSON schema types.
59
58
  #
60
- # @param type [Class] The Ruby type to map.
59
+ # @param type [String] The Ruby type to map.
61
60
  # @return [String] The corresponding JSON schema type.
62
61
  # @raise [ArgumentError] If the provided type is not supported.
63
62
  def self.type_mapping(type)
64
63
  case type
65
- when "String"
66
- 'string'
67
- when "Integer"
68
- 'integer'
69
- when "Float"
70
- 'number'
71
- when "TrueClass", "FalseClass"
72
- 'boolean'
73
- when "Array"
74
- 'array'
75
- when "Hash"
76
- 'object'
77
- when "NilClass"
78
- 'null'
64
+ when "String" then "string"
65
+ when "Integer" then "integer"
66
+ when "Float" then "number"
67
+ when "TrueClass", "FalseClass" then "boolean"
68
+ when "Array" then "array"
69
+ when "Hash" then "object"
70
+ when "NilClass" then "null"
79
71
  else
80
- # raise ArgumentError, "Unsupported type: #{type} #{type.class}"
81
- 'string'
72
+ raise ArgumentError, "Unsupported type: #{type} #{type.class}"
82
73
  end
83
74
  end
84
75
  end
85
76
 
86
77
  class UnboundMethod
78
+ # Converts an UnboundMethod to a JSON schema.
79
+ #
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
+ # @return [String] The JSON schema representation of the method.
113
+ def to_json_schema
114
+ ToolTailor.convert(self)
115
+ end
116
+ end
117
+
118
+ class Method
119
+ # Converts a Method to a JSON schema.
120
+ #
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
+ # @return [String] The JSON schema representation of the method.
87
154
  def to_json_schema
88
155
  ToolTailor.convert(self)
89
156
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tool_tailor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kieran Klaassen