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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c41c79bec1d11195ab214396b4b9d1f011585f3d8e27e093ee0e28d149630fb
4
- data.tar.gz: bd0b91cdc281a50b2c5b2805d715f318eb0b7e58ca733d83c2093f5f26ef9bc4
3
+ metadata.gz: 1d3bbdddbe89db30b52f4fa021dd6b7ddbf38049e985d8a5ec57dd28bfed4f9d
4
+ data.tar.gz: cb046b9026c87e727b4bb6231eef4e288c574f625cf57ea9646839599e353772
5
5
  SHA512:
6
- metadata.gz: 2ebb21547acbd24b2212db7a0942cc2be6c142a707a7e3bc60fc27075c0873c57ed8b48bb758282ba875a6ac86bd7dbc8e914d0514e058ddfc64cb9cef5a6984
7
- data.tar.gz: 8b09995ec0e4d5ee9037d75478be3b170ea441c4b9f9bd01882e2e4de42ffbe81cf9e93ce598d01a907cbd05d6e514c1d4992ab370e64508af46004a410d5746
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
- # Create a chat completion
51
- response = client.chat(
52
- parameters: {
53
- model: "gpt-3.5-turbo",
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
- # Handle the response
60
- tool_handler.handle(response: response)
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(api_key: OPENAI_API_KEY)
23
+ client = OpenAI::Client.new
23
24
 
24
25
  # Create a chat completion
25
26
  response = client.chat.completions.create(
26
- model: "gpt-3.5-turbo",
27
- messages: [{ role: "user", content: "What's the weather like in Boston?" }],
28
- tools: [weather_tool.to_json]
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
- tool_call = response.dig("choices", 0, "message", "tool_calls", 0)
16
- return unless tool_call
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
- function_name = tool_call.dig("function", "name")
19
- arguments = JSON.parse(tool_call.dig("function", "arguments"))
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Openai
4
4
  module Toolable
5
- VERSION = "0.1.1"
5
+ VERSION = "0.1.7"
6
6
  end
7
7
  end
@@ -7,9 +7,9 @@ module Openai
7
7
  class Error < StandardError; end
8
8
 
9
9
  class Tool
10
- attr_reader :name, :type, :description, :parameters
10
+ attr_reader :name, :description, :parameters, :type
11
11
 
12
- def initialize(name:, type:, description:, parameters: [])
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
- name: name,
29
- type: type,
30
- description: description,
31
- parameters: {
32
- type: :object,
33
- properties: parameters.each_with_object({}) do |param, hash|
34
- hash[param.name] = { type: param.type, description: param.description }
35
- end,
36
- required: parameters.select(&:required).map(&:name)
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:, type:, description:, parameters: [])
44
- Tool.new(name: name, type: type, description: description, parameters: parameters)
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.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