Skip to content

Commit 7e492d7

Browse files
committed
responses chat session parity with regular completions chat session, rewrite example to match
1 parent 9745198 commit 7e492d7

4 files changed

Lines changed: 253 additions & 68 deletions

File tree

examples/responses/tools.lua

Lines changed: 42 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,44 @@
1-
-- This shows how to use tool calls with the Responses API
1+
-- This shows how to use tool calls with the Responses API chat session,
2+
-- including per-request tool_choice overrides to control tool usage.
23

34
local openai = require("openai")
45
local cjson = require("cjson")
56

67
local client = openai.new(os.getenv("OPENAI_API_KEY"))
78

8-
-- Define a simple weather tool
9+
-- Define a simple addition tool
910
local tools = {
1011
{
1112
type = "function",
12-
name = "get_weather",
13-
description = "Get the current weather for a location",
13+
name = "add_numbers",
14+
description = "Add two numbers and return the sum.",
1415
parameters = {
1516
type = "object",
1617
properties = {
17-
location = {
18-
type = "string",
19-
description = "The city and state, e.g. San Francisco, CA"
20-
},
21-
unit = {
22-
type = "string",
23-
enum = {"celsius", "fahrenheit"},
24-
description = "The temperature unit"
25-
}
18+
a = { type = "number" },
19+
b = { type = "number" }
2620
},
27-
required = {"location"}
21+
required = { "a", "b" }
2822
}
2923
}
3024
}
3125

32-
-- Simulate getting weather data
33-
local function get_weather(location, unit)
34-
unit = unit or "fahrenheit"
35-
-- In a real app, you'd call a weather API here
26+
-- Simulate the tool locally
27+
local function add_numbers(a, b)
3628
return {
37-
location = location,
38-
temperature = unit == "celsius" and 22 or 72,
39-
unit = unit,
40-
conditions = "sunny"
29+
sum = a + b,
30+
explanation = ("The sum of %d and %d is %d."):format(a, b, a + b)
4131
}
4232
end
4333

4434
local session = client:new_responses_chat_session({
45-
tools = tools
35+
tools = tools,
36+
instructions = "You are a careful math assistant. Always call the provided tool to do arithmetic."
4637
})
4738

48-
print("Asking about weather...")
49-
local response, err, raw = session:send("What's the weather like in San Francisco?")
39+
-- Send initial message, forcing a tool call
40+
print("Sending initial message...")
41+
local response, err, raw = session:send("What is 123 + 456? Respond with the total.", {tool_choice = "required"})
5042

5143
if not response then
5244
print("Error:", err)
@@ -62,37 +54,34 @@ for _, output_item in ipairs(response.output or {}) do
6254
end
6355
end
6456

65-
if #tool_calls > 0 then
66-
print("Model requested tool calls:")
67-
68-
for _, tool_call in ipairs(tool_calls) do
69-
print(" - Function:", tool_call.name)
70-
print(" Arguments:", cjson.encode(tool_call.arguments))
57+
if #tool_calls == 0 then
58+
print("Error: expected a tool call response")
59+
os.exit(1)
60+
end
7161

72-
-- Execute the tool
73-
if tool_call.name == "get_weather" then
74-
local args = tool_call.arguments
75-
local result = get_weather(args.location, args.unit)
62+
print("Model requested tool calls:")
63+
for _, tool_call in ipairs(tool_calls) do
64+
print(" - Function:", tool_call.name)
65+
print(" Arguments:", tool_call.arguments)
7666

77-
print(" Result:", cjson.encode(result))
67+
-- Execute the tool (arguments is a JSON string, decode it first)
68+
local args = cjson.decode(tool_call.arguments)
69+
local result = add_numbers(args.a, args.b)
70+
print(" Result:", cjson.encode(result))
7871

79-
-- Send the tool result back
80-
local follow_up, err2 = session:send({
81-
{
82-
type = "function_call_output",
83-
call_id = tool_call.call_id,
84-
output = cjson.encode(result)
85-
}
86-
})
72+
-- Send the tool result back, forcing a text response (no more tool calls)
73+
local final, err2 = session:send({
74+
{
75+
type = "function_call_output",
76+
call_id = tool_call.call_id,
77+
output = cjson.encode(result)
78+
}
79+
}, {tool_choice = "none"})
8780

88-
if follow_up then
89-
print("\nFinal response:", follow_up:get_output_text())
90-
else
91-
print("Error sending tool result:", err2)
92-
end
93-
end
81+
if not final then
82+
print("Error sending tool result:", err2)
83+
os.exit(1)
9484
end
95-
else
96-
-- No tool calls, just print the response
97-
print("Response:", response:get_output_text())
85+
86+
print("\nFinal response:", final:get_output_text())
9887
end

openai/responses.lua

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,20 @@ local input_content_item = types.one_of({
8686
filename = types.string
8787
})
8888
})
89-
local input_format = types.string + types.array_of(types.partial({
89+
local function_call_output_item = types.partial({
90+
type = types.literal("function_call_output"),
91+
call_id = types.string,
92+
output = types.string
93+
})
94+
local input_message = types.partial({
9095
role = types.one_of({
9196
"system",
9297
"user",
9398
"assistant"
9499
}),
95100
content = types.string + types.array_of(input_content_item)
96-
}))
101+
})
102+
local input_format = types.string + types.array_of(input_message + function_call_output_item)
97103
local content_item = types.one_of({
98104
types.partial({
99105
type = "output_text",
@@ -121,10 +127,19 @@ local response_message = types.partial({
121127
content = types.array_of(content_item),
122128
status = empty + types.string
123129
})
130+
local function_call_item = types.partial({
131+
id = empty + types.string,
132+
type = types.literal("function_call"),
133+
name = types.string,
134+
arguments = types.string,
135+
call_id = types.string,
136+
status = empty + types.string
137+
})
138+
local output_item = response_message + function_call_item
124139
local parse_responses_response = types.partial({
125140
id = types.string:tag("id"),
126141
object = empty + types.literal("response"):tag("object"),
127-
output = types.array_of(response_message):tag("output"),
142+
output = types.array_of(output_item):tag("output"),
128143
model = empty + types.string:tag("model"),
129144
usage = empty + types.table:tag("usage"),
130145
status = empty + types.string:tag("status")
@@ -133,14 +148,26 @@ local ResponsesChatSession
133148
do
134149
local _class_0
135150
local _base_0 = {
136-
send = function(self, input, stream_callback)
137-
if stream_callback == nil then
138-
stream_callback = nil
151+
send = function(self, input, opts)
152+
if opts == nil then
153+
opts = { }
154+
end
155+
if type(opts) == "function" then
156+
opts = {
157+
stream_callback = opts
158+
}
139159
end
140-
return self:create_response(input, {
160+
local stream_callback = opts.stream_callback
161+
local request_opts = {
141162
previous_response_id = self.current_response_id,
142163
stream = stream_callback and true or nil
143-
}, stream_callback)
164+
}
165+
for k, v in pairs(opts) do
166+
if k ~= "stream_callback" then
167+
request_opts[k] = v
168+
end
169+
end
170+
return self:create_response(input, request_opts, stream_callback)
144171
end,
145172
create_response = function(self, input, opts, stream_callback)
146173
if opts == nil then

openai/responses.moon

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,22 @@ input_content_item = types.one_of {
5454
types.partial { type: "input_file", file_data: types.string, filename: types.string } -- base64 encoded
5555
}
5656

57-
-- Schema for validating input parameter which can be string or array of messages
58-
input_format = types.string + types.array_of types.partial {
57+
-- Schema for validating function call output items (tool results sent back)
58+
function_call_output_item = types.partial {
59+
type: types.literal("function_call_output")
60+
call_id: types.string
61+
output: types.string
62+
}
63+
64+
-- Schema for input messages with a role
65+
input_message = types.partial {
5966
role: types.one_of {"system", "user", "assistant"}
6067
content: types.string + types.array_of(input_content_item)
6168
}
6269

70+
-- Schema for validating input parameter which can be string, array of messages, or array of tool results
71+
input_format = types.string + types.array_of(input_message + function_call_output_item)
72+
6373
-- Schema for validating response content items
6474
content_item = types.one_of {
6575
types.partial {
@@ -90,11 +100,22 @@ response_message = types.partial {
90100
status: empty + types.string
91101
}
92102

103+
function_call_item = types.partial {
104+
id: empty + types.string
105+
type: types.literal("function_call")
106+
name: types.string
107+
arguments: types.string
108+
call_id: types.string
109+
status: empty + types.string
110+
}
111+
112+
output_item = response_message + function_call_item
113+
93114
-- Schema for validating complete response structure
94115
parse_responses_response = types.partial {
95116
id: types.string\tag "id"
96117
object: empty + types.literal("response")\tag "object"
97-
output: types.array_of(response_message)\tag "output"
118+
output: types.array_of(output_item)\tag "output"
98119
model: empty + types.string\tag "model"
99120
usage: empty + types.table\tag "usage"
100121
status: empty + types.string\tag "status"
@@ -108,12 +129,24 @@ class ResponsesChatSession
108129

109130
-- Send input and get response, maintaining conversation state
110131
-- input: string or array of message objects
111-
-- stream_callback: optional function for streaming responses
112-
send: (input, stream_callback=nil) =>
113-
@create_response input, {
132+
-- opts: optional table with stream_callback and/or per-request overrides
133+
-- (passing a function directly is supported for backward compatibility)
134+
send: (input, opts={}) =>
135+
if type(opts) == "function"
136+
opts = { stream_callback: opts }
137+
138+
stream_callback = opts.stream_callback
139+
140+
request_opts = {
114141
previous_response_id: @current_response_id
115142
stream: stream_callback and true or nil
116-
}, stream_callback
143+
}
144+
145+
for k, v in pairs opts
146+
if k != "stream_callback"
147+
request_opts[k] = v
148+
149+
@create_response input, request_opts, stream_callback
117150

118151
-- Create a response using the Responses API
119152
-- input: string or array of message objects

0 commit comments

Comments
 (0)