@@ -44,13 +44,15 @@ def to_tool_call
4444 end
4545 end
4646
47- attr_reader :tool_calls , :input_tokens , :output_tokens
47+ attr_reader :tool_calls , :input_tokens , :output_tokens , :output_thinking
4848
49- def initialize ( streaming_mode :, partial_tool_calls : false )
49+ def initialize ( streaming_mode :, partial_tool_calls : false , output_thinking : false )
5050 @streaming_mode = streaming_mode
5151 @tool_calls = [ ]
5252 @current_tool_call = nil
5353 @partial_tool_calls = partial_tool_calls
54+ @output_thinking = output_thinking
55+ @thinking = nil
5456 end
5557
5658 def to_tool_calls
@@ -69,13 +71,48 @@ def process_streamed_message(parsed)
6971 tool_id ,
7072 partial_tool_calls : @partial_tool_calls ,
7173 ) if tool_name
74+ elsif parsed [ :type ] == "content_block_start" && parsed . dig ( :content_block , :type ) == "thinking"
75+ if @output_thinking
76+ @thinking =
77+ DiscourseAi ::Completions ::Thinking . new (
78+ message : +parsed . dig ( :content_block , :thinking ) . to_s ,
79+ signature : +"" ,
80+ partial : true ,
81+ )
82+ result = @thinking . dup
83+ end
84+ elsif parsed [ :type ] == "content_block_delta" && parsed . dig ( :delta , :type ) == "thinking_delta"
85+ if @output_thinking
86+ delta = parsed . dig ( :delta , :thinking )
87+ @thinking . message << delta if @thinking
88+ result = DiscourseAi ::Completions ::Thinking . new ( message : delta , partial : true )
89+ end
90+ elsif parsed [ :type ] == "content_block_delta" && parsed . dig ( :delta , :type ) == "signature_delta"
91+ if @output_thinking
92+ @thinking . signature << parsed . dig ( :delta , :signature ) if @thinking
93+ end
94+ elsif parsed [ :type ] == "content_block_stop" && @thinking
95+ @thinking . partial = false
96+ result = @thinking
97+ @thinking = nil
7298 elsif parsed [ :type ] == "content_block_start" || parsed [ :type ] == "content_block_delta"
7399 if @current_tool_call
74100 tool_delta = parsed . dig ( :delta , :partial_json ) . to_s
75101 @current_tool_call . append ( tool_delta )
76102 result = @current_tool_call . partial_tool_call if @current_tool_call . has_partial?
103+ elsif parsed . dig ( :content_block , :type ) == "redacted_thinking"
104+ if @output_thinking
105+ result =
106+ DiscourseAi ::Completions ::Thinking . new (
107+ message : nil ,
108+ signature : parsed . dig ( :content_block , :data ) ,
109+ redacted : true ,
110+ )
111+ end
77112 else
78113 result = parsed . dig ( :delta , :text ) . to_s
114+ # no need to return empty strings for streaming, no value
115+ result = nil if result == ""
79116 end
80117 elsif parsed [ :type ] == "content_block_stop"
81118 if @current_tool_call
@@ -105,15 +142,32 @@ def process_message(payload)
105142 content = parsed . dig ( :content )
106143 if content . is_a? ( Array )
107144 result =
108- content . map do |data |
109- if data [ :type ] == "tool_use"
110- call = AnthropicToolCall . new ( data [ :name ] , data [ :id ] )
111- call . append ( data [ :input ] . to_json )
112- call . to_tool_call
113- else
114- data [ :text ]
145+ content
146+ . map do |data |
147+ if data [ :type ] == "tool_use"
148+ call = AnthropicToolCall . new ( data [ :name ] , data [ :id ] )
149+ call . append ( data [ :input ] . to_json )
150+ call . to_tool_call
151+ elsif data [ :type ] == "thinking"
152+ if @output_thinking
153+ DiscourseAi ::Completions ::Thinking . new (
154+ message : data [ :thinking ] ,
155+ signature : data [ :signature ] ,
156+ )
157+ end
158+ elsif data [ :type ] == "redacted_thinking"
159+ if @output_thinking
160+ DiscourseAi ::Completions ::Thinking . new (
161+ message : nil ,
162+ signature : data [ :data ] ,
163+ redacted : true ,
164+ )
165+ end
166+ else
167+ data [ :text ]
168+ end
115169 end
116- end
170+ . compact
117171 end
118172
119173 @input_tokens = parsed . dig ( :usage , :input_tokens )
0 commit comments