Skip to content

Commit 4d79701

Browse files
renfredxhstainless-app[bot]
authored andcommitted
feat: add output_text method for non-streaming responses (#757)
1 parent be54112 commit 4d79701

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

lib/openai/models/responses/response.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,26 @@ class Response < OpenAI::Internal::Type::BaseModel
253253
# @return [String, nil]
254254
optional :user, String
255255

256+
# Convenience property that aggregates all `output_text` items from the `output` list.
257+
#
258+
# If no `output_text` content blocks exist, then an empty string is returned.
259+
#
260+
# @return [String]
261+
def output_text
262+
texts = []
263+
264+
output.each do |item|
265+
next unless item.type == :message
266+
item.content.each do |content|
267+
if content.type == :output_text
268+
texts << content.text
269+
end
270+
end
271+
end
272+
273+
texts.join
274+
end
275+
256276
# @!method initialize(id:, created_at:, error:, incomplete_details:, instructions:, metadata:, model:, output:, parallel_tool_calls:, temperature:, tool_choice:, tools:, top_p:, background: nil, max_output_tokens: nil, max_tool_calls: nil, previous_response_id: nil, prompt: nil, reasoning: nil, service_tier: nil, status: nil, text: nil, top_logprobs: nil, truncation: nil, usage: nil, user: nil, object: :response)
257277
# Some parameter documentations has been truncated, see
258278
# {OpenAI::Models::Responses::Response} for more details.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../../test_helper"
4+
5+
class OpenAI::Models::Responses::ResponseTest < Minitest::Test
6+
def test_output_text_with_single_message
7+
response = build_response(
8+
output: [
9+
message_output(text: "Hello, world!")
10+
]
11+
)
12+
13+
assert_equal("Hello, world!", response.output_text)
14+
end
15+
16+
def test_output_text_with_multiple_messages
17+
response = build_response(
18+
output: [
19+
message_output(text: "First part. "),
20+
message_output(text: "Second part.")
21+
]
22+
)
23+
24+
assert_equal("First part. Second part.", response.output_text)
25+
end
26+
27+
def test_output_text_with_multiple_content_blocks
28+
response = build_response(
29+
output: [
30+
message_output(
31+
content: [
32+
text_content("Part A"),
33+
text_content(" and Part B")
34+
]
35+
)
36+
]
37+
)
38+
39+
assert_equal("Part A and Part B", response.output_text)
40+
end
41+
42+
def test_output_text_with_no_messages
43+
response = build_response(output: [])
44+
45+
assert_equal("", response.output_text)
46+
end
47+
48+
def test_output_text_with_non_message_output_items
49+
response = build_response(
50+
output: [
51+
function_call_output("test_function", "{}"),
52+
message_output(text: "After tool call")
53+
]
54+
)
55+
56+
assert_equal("After tool call", response.output_text)
57+
end
58+
59+
def test_output_text_with_refusal_content
60+
response = build_response(
61+
output: [
62+
message_output(
63+
content: [
64+
refusal_content("I cannot do that"),
65+
text_content("But I can help with this")
66+
]
67+
)
68+
]
69+
)
70+
71+
assert_equal("But I can help with this", response.output_text)
72+
end
73+
74+
def test_output_text_with_empty_text
75+
response = build_response(
76+
output: [message_output(text: "")]
77+
)
78+
79+
assert_equal("", response.output_text)
80+
end
81+
82+
def test_output_text_with_mixed_empty_and_non_empty_text
83+
response = build_response(
84+
output: [
85+
message_output(
86+
content: [
87+
text_content(""),
88+
text_content("Non-empty"),
89+
text_content("")
90+
]
91+
)
92+
]
93+
)
94+
95+
assert_equal("Non-empty", response.output_text)
96+
end
97+
98+
private
99+
100+
def build_response(output:)
101+
OpenAI::Responses::Response.new(
102+
id: "resp_test",
103+
created_at: Time.now.to_f,
104+
model: "gpt-4o",
105+
object: :response,
106+
parallel_tool_calls: false,
107+
tool_choice: :auto,
108+
tools: [],
109+
output: output
110+
)
111+
end
112+
113+
def message_output(text: nil, content: nil)
114+
content ||= [text_content(text)] if text
115+
116+
OpenAI::Responses::ResponseOutputMessage.new(
117+
id: "msg_#{SecureRandom.hex(4)}",
118+
status: :completed,
119+
content: content
120+
)
121+
end
122+
123+
def text_content(text)
124+
OpenAI::Responses::ResponseOutputText.new(
125+
text: text,
126+
annotations: []
127+
)
128+
end
129+
130+
def refusal_content(refusal)
131+
OpenAI::Responses::ResponseOutputRefusal.new(
132+
refusal: refusal
133+
)
134+
end
135+
136+
def function_call_output(name, arguments)
137+
OpenAI::Responses::ResponseFunctionToolCall.new(
138+
call_id: "call_#{SecureRandom.hex(4)}",
139+
name: name,
140+
arguments: arguments,
141+
parsed: {}
142+
)
143+
end
144+
end

0 commit comments

Comments
 (0)