11# frozen_string_literal: true
22
33class DiscourseAi ::Completions ::AnthropicMessageProcessor
4+ class ToolCallProgressTracker
5+ attr_reader :current_key , :current_value , :tool_call
6+
7+ def initialize ( tool_call )
8+ @tool_call = tool_call
9+ @current_key = nil
10+ @current_value = nil
11+ @parser = DiscourseAi ::Utils ::JsonStreamingParser . new
12+
13+ @parser . key do |k |
14+ @current_key = k
15+ @current_value = nil
16+ end
17+ @parser . value do |v |
18+ @current_value = v
19+
20+ if @current_key
21+ tool_call . tool_progress . call (
22+ { name : tool_call . name , id : tool_call . id , key : @current_key , value : @current_value } ,
23+ )
24+ end
25+ end
26+ end
27+
28+ def <<( json )
29+ # llm could send broken json
30+ # in that case just deal with it later
31+ # don't stream
32+ return if @broken
33+
34+ begin
35+ @parser << json
36+ rescue DiscourseAi ::Utils ::ParserError
37+ @broken = true
38+ return
39+ end
40+
41+ if @parser . state == :start_string && @current_key
42+ # this is is worth notifying
43+ tool_call . tool_progress . call (
44+ {
45+ name : tool_call . name ,
46+ id : tool_call . id ,
47+ key : @current_key ,
48+ value : @parser . buf ,
49+ done : false ,
50+ } ,
51+ )
52+ end
53+ end
54+ end
55+
456 class AnthropicToolCall
5- attr_reader :name , :raw_json , :id
57+ attr_reader :name , :raw_json , :id , :tool_progress
658
7- def initialize ( name , id )
59+ def initialize ( name , id , tool_progress )
860 @name = name
961 @id = id
1062 @raw_json = +""
63+ if tool_progress
64+ @tool_progress = tool_progress
65+ @tool_call_progress_tracker = ToolCallProgressTracker . new ( self )
66+ end
1167 end
1268
1369 def append ( json )
1470 @raw_json << json
71+ @tool_call_progress_tracker << json if @tool_progress
1572 end
1673 end
1774
1875 attr_reader :tool_calls , :input_tokens , :output_tokens
1976
20- def initialize ( streaming_mode :)
77+ def initialize ( streaming_mode :, tool_progress : )
2178 @streaming_mode = streaming_mode
2279 @tool_calls = [ ]
80+ @tool_progress = tool_progress
2381 end
2482
2583 def to_xml_tool_calls ( function_buffer )
@@ -58,7 +116,7 @@ def process_message(payload)
58116 if parsed [ :type ] == "content_block_start" && parsed . dig ( :content_block , :type ) == "tool_use"
59117 tool_name = parsed . dig ( :content_block , :name )
60118 tool_id = parsed . dig ( :content_block , :id )
61- @tool_calls << AnthropicToolCall . new ( tool_name , tool_id ) if tool_name
119+ @tool_calls << AnthropicToolCall . new ( tool_name , tool_id , @tool_progress ) if tool_name
62120 elsif parsed [ :type ] == "content_block_start" || parsed [ :type ] == "content_block_delta"
63121 if @tool_calls . present?
64122 result = parsed . dig ( :delta , :partial_json ) . to_s
@@ -83,7 +141,7 @@ def process_message(payload)
83141 if content . is_a? ( Array )
84142 tool_call = content . find { |c | c [ :type ] == "tool_use" }
85143 if tool_call
86- @tool_calls << AnthropicToolCall . new ( tool_call [ :name ] , tool_call [ :id ] )
144+ @tool_calls << AnthropicToolCall . new ( tool_call [ :name ] , tool_call [ :id ] , @tool_progress )
87145 @tool_calls . last . append ( tool_call [ :input ] . to_json )
88146 else
89147 result = parsed . dig ( :content , 0 , :text ) . to_s
0 commit comments