Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5e524ea

Browse filesBrowse files
authored
feat: structured output for responses API (tools) (#691)
1 parent 282ec24 commit 5e524ea
Copy full SHA for 5e524ea

File tree

Expand file treeCollapse file tree

7 files changed

+77
-3
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+77
-3
lines changed
+28Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require_relative "../lib/openai"
5+
6+
class GetWeather < OpenAI::BaseModel
7+
required :location, String, doc: "City and country e.g. Bogotá, Colombia"
8+
end
9+
10+
# gets API Key from environment variable `OPENAI_API_KEY`
11+
client = OpenAI::Client.new
12+
13+
response = client.responses.create(
14+
model: "gpt-4o-2024-08-06",
15+
input: [
16+
{
17+
role: :user,
18+
content: "What's the weather like in Paris today?"
19+
}
20+
],
21+
tools: [GetWeather]
22+
)
23+
24+
response
25+
.output
26+
.each do |output|
27+
pp(output.parsed)
28+
end

‎lib/openai/models/responses/function_tool.rb

Copy file name to clipboardExpand all lines: lib/openai/models/responses/function_tool.rb
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ class FunctionTool < OpenAI::Internal::Type::BaseModel
1414
# A JSON schema object describing the parameters of the function.
1515
#
1616
# @return [Hash{Symbol=>Object}, nil]
17-
required :parameters, OpenAI::Internal::Type::HashOf[OpenAI::Internal::Type::Unknown], nil?: true
17+
required :parameters,
18+
union: OpenAI::UnionOf[
19+
OpenAI::Internal::Type::HashOf[OpenAI::Internal::Type::Unknown],
20+
OpenAI::StructuredOutput::JsonSchemaConverter
21+
],
22+
nil?: true
1823

1924
# @!attribute strict
2025
# Whether to enforce strict parameter validation. Default `true`.

‎lib/openai/models/responses/response_create_params.rb

Copy file name to clipboardExpand all lines: lib/openai/models/responses/response_create_params.rb
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class ResponseCreateParams < OpenAI::Internal::Type::BaseModel
191191
# the model to call your own code. Learn more about
192192
# [function calling](https://platform.openai.com/docs/guides/function-calling).
193193
#
194-
# @return [Array<OpenAI::Models::Responses::FunctionTool, OpenAI::Models::Responses::FileSearchTool, OpenAI::Models::Responses::ComputerTool, OpenAI::Models::Responses::Tool::Mcp, OpenAI::Models::Responses::Tool::CodeInterpreter, OpenAI::Models::Responses::Tool::ImageGeneration, OpenAI::Models::Responses::Tool::LocalShell, OpenAI::Models::Responses::WebSearchTool>, nil]
194+
# @return [Array<OpenAI::Models::Responses::FunctionTool, OpenAI::StructuredOutput::JsonSchemaConverter, OpenAI::Models::Responses::FileSearchTool, OpenAI::Models::Responses::ComputerTool, OpenAI::Models::Responses::Tool::Mcp, OpenAI::Models::Responses::Tool::CodeInterpreter, OpenAI::Models::Responses::Tool::ImageGeneration, OpenAI::Models::Responses::Tool::LocalShell, OpenAI::Models::Responses::WebSearchTool>, nil]
195195
optional :tools, -> { OpenAI::Internal::Type::ArrayOf[union: OpenAI::Responses::Tool] }
196196

197197
# @!attribute top_p
@@ -258,7 +258,7 @@ class ResponseCreateParams < OpenAI::Internal::Type::BaseModel
258258
#
259259
# @param tool_choice [Symbol, OpenAI::Models::Responses::ToolChoiceOptions, OpenAI::Models::Responses::ToolChoiceTypes, OpenAI::Models::Responses::ToolChoiceFunction] How the model should select which tool (or tools) to use when generating
260260
#
261-
# @param tools [Array<OpenAI::Models::Responses::FunctionTool, OpenAI::Models::Responses::FileSearchTool, OpenAI::Models::Responses::ComputerTool, OpenAI::Models::Responses::Tool::Mcp, OpenAI::Models::Responses::Tool::CodeInterpreter, OpenAI::Models::Responses::Tool::ImageGeneration, OpenAI::Models::Responses::Tool::LocalShell, OpenAI::Models::Responses::WebSearchTool>] An array of tools the model may call while generating a response. You
261+
# @param tools [Array<OpenAI::Models::Responses::FunctionTool, OpenAI::StructuredOutput::JsonSchemaConverter, OpenAI::Models::Responses::FileSearchTool, OpenAI::Models::Responses::ComputerTool, OpenAI::Models::Responses::Tool::Mcp, OpenAI::Models::Responses::Tool::CodeInterpreter, OpenAI::Models::Responses::Tool::ImageGeneration, OpenAI::Models::Responses::Tool::LocalShell, OpenAI::Models::Responses::WebSearchTool>] An array of tools the model may call while generating a response. You
262262
#
263263
# @param top_p [Float, nil] An alternative to sampling with temperature, called nucleus sampling,
264264
#

‎lib/openai/models/responses/response_function_tool_call.rb

Copy file name to clipboardExpand all lines: lib/openai/models/responses/response_function_tool_call.rb
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ class ResponseFunctionToolCall < OpenAI::Internal::Type::BaseModel
1010
# @return [String]
1111
required :arguments, String
1212

13+
# @!attribute parsed
14+
# The parsed contents of the arguments.
15+
#
16+
# @return [Object, nil]
17+
required :parsed, OpenAI::Internal::Type::Unknown
18+
1319
# @!attribute call_id
1420
# The unique ID of the function tool call generated by the model.
1521
#

‎lib/openai/models/responses/tool.rb

Copy file name to clipboardExpand all lines: lib/openai/models/responses/tool.rb
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ module Tool
1212
# Defines a function in your own code the model can choose to call. Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling).
1313
variant :function, -> { OpenAI::Responses::FunctionTool }
1414

15+
variant -> { OpenAI::StructuredOutput::JsonSchemaConverter }
16+
1517
# A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search).
1618
variant :file_search, -> { OpenAI::Responses::FileSearchTool }
1719

‎lib/openai/resources/responses.rb

Copy file name to clipboardExpand all lines: lib/openai/resources/responses.rb
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def create(params)
7676
end
7777

7878
model = nil
79+
tool_models = {}
7980
case parsed
8081
in {text: OpenAI::StructuredOutput::JsonSchemaConverter => model}
8182
parsed.update(
@@ -99,6 +100,27 @@ def create(params)
99100
)
100101
in {text: {format: {type: :json_schema, schema: OpenAI::StructuredOutput::JsonSchemaConverter => model}}}
101102
parsed.dig(:text, :format).store(:schema, model.to_json_schema)
103+
in {tools: Array => tools}
104+
mapped = tools.map do |tool|
105+
case tool
106+
in OpenAI::StructuredOutput::JsonSchemaConverter
107+
name = tool.name.split("::").last
108+
tool_models.store(name, tool)
109+
{
110+
type: :function,
111+
strict: true,
112+
name: name,
113+
parameters: tool.to_json_schema
114+
}
115+
in {type: :function, parameters: OpenAI::StructuredOutput::JsonSchemaConverter => params}
116+
func = tool.fetch(:function)
117+
name = func[:name] ||= params.name.split("::").last
118+
tool_models.store(name, params)
119+
func.update(parameters: params.to_json_schema)
120+
else
121+
end
122+
end
123+
tools.replace(mapped)
102124
else
103125
end
104126

@@ -116,6 +138,13 @@ def create(params)
116138
content.store(:parsed, coerced)
117139
end
118140
end
141+
raw[:output]&.each do |output|
142+
next unless output[:type] == "function_call"
143+
next if (model = tool_models[output.fetch(:name)]).nil?
144+
parsed = JSON.parse(output.fetch(:arguments), symbolize_names: true)
145+
coerced = OpenAI::Internal::Type::Converter.coerce(model, parsed)
146+
output.store(:parsed, coerced)
147+
end
119148

120149
raw
121150
end

‎rbi/openai/models/responses/response_function_tool_call.rbi

Copy file name to clipboardExpand all lines: rbi/openai/models/responses/response_function_tool_call.rbi
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ module OpenAI
1616
sig { returns(String) }
1717
attr_accessor :arguments
1818

19+
# The parsed contents of the arguments.
20+
sig { returns(T.anything) }
21+
attr_accessor :parsed
22+
1923
# The unique ID of the function tool call generated by the model.
2024
sig { returns(String) }
2125
attr_accessor :call_id

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.