@@ -13,87 +13,81 @@ def initialize(name, id)
1313 def append ( json )
1414 @raw_json << json
1515 end
16+
17+ def to_tool_call
18+ parameters = JSON . parse ( raw_json , symbolize_names : true )
19+ DiscourseAi ::Completions ::ToolCall . new ( id : id , name : name , parameters : parameters )
20+ end
1621 end
1722
1823 attr_reader :tool_calls , :input_tokens , :output_tokens
1924
2025 def initialize ( streaming_mode :)
2126 @streaming_mode = streaming_mode
2227 @tool_calls = [ ]
28+ @current_tool_call = nil
2329 end
2430
25- def to_xml_tool_calls ( function_buffer )
26- return function_buffer if @tool_calls . blank?
27-
28- function_buffer = Nokogiri ::HTML5 . fragment ( <<~TEXT )
29- <function_calls>
30- </function_calls>
31- TEXT
32-
33- @tool_calls . each do |tool_call |
34- node =
35- function_buffer . at ( "function_calls" ) . add_child (
36- Nokogiri ::HTML5 ::DocumentFragment . parse (
37- DiscourseAi ::Completions ::Endpoints ::Base . noop_function_call_text + "\n " ,
38- ) ,
39- )
40-
41- params = JSON . parse ( tool_call . raw_json , symbolize_names : true )
42- xml =
43- params . map { |name , value | "<#{ name } >#{ CGI . escapeHTML ( value . to_s ) } </#{ name } >" } . join ( "\n " )
31+ def to_tool_calls
32+ @tool_calls . map { |tool_call | tool_call . to_tool_call }
33+ end
4434
45- node . at ( "tool_name" ) . content = tool_call . name
46- node . at ( "tool_id" ) . content = tool_call . id
47- node . at ( "parameters" ) . children = Nokogiri ::HTML5 ::DocumentFragment . parse ( xml ) if xml . present?
35+ def process_streamed_message ( parsed )
36+ result = nil
37+ if parsed [ :type ] == "content_block_start" && parsed . dig ( :content_block , :type ) == "tool_use"
38+ tool_name = parsed . dig ( :content_block , :name )
39+ tool_id = parsed . dig ( :content_block , :id )
40+ result = @current_tool_call . to_tool_call if @current_tool_call
41+ @current_tool_call = AnthropicToolCall . new ( tool_name , tool_id ) if tool_name
42+ elsif parsed [ :type ] == "content_block_start" || parsed [ :type ] == "content_block_delta"
43+ if @current_tool_call
44+ tool_delta = parsed . dig ( :delta , :partial_json ) . to_s
45+ @current_tool_call . append ( tool_delta )
46+ else
47+ result = parsed . dig ( :delta , :text ) . to_s
48+ end
49+ elsif parsed [ :type ] == "content_block_stop"
50+ if @current_tool_call
51+ result = @current_tool_call . to_tool_call
52+ @current_tool_call = nil
53+ end
54+ elsif parsed [ :type ] == "message_start"
55+ @input_tokens = parsed . dig ( :message , :usage , :input_tokens )
56+ elsif parsed [ :type ] == "message_delta"
57+ @output_tokens =
58+ parsed . dig ( :usage , :output_tokens ) || parsed . dig ( :delta , :usage , :output_tokens )
59+ elsif parsed [ :type ] == "message_stop"
60+ # bedrock has this ...
61+ if bedrock_stats = parsed . dig ( "amazon-bedrock-invocationMetrics" . to_sym )
62+ @input_tokens = bedrock_stats [ :inputTokenCount ] || @input_tokens
63+ @output_tokens = bedrock_stats [ :outputTokenCount ] || @output_tokens
64+ end
4865 end
49-
50- function_buffer
66+ result
5167 end
5268
5369 def process_message ( payload )
5470 result = ""
55- parsed = JSON . parse ( payload , symbolize_names : true )
71+ parsed = payload
72+ parsed = JSON . parse ( payload , symbolize_names : true ) if payload . is_a? ( String )
5673
57- if @streaming_mode
58- if parsed [ :type ] == "content_block_start" && parsed . dig ( :content_block , :type ) == "tool_use"
59- tool_name = parsed . dig ( :content_block , :name )
60- tool_id = parsed . dig ( :content_block , :id )
61- @tool_calls << AnthropicToolCall . new ( tool_name , tool_id ) if tool_name
62- elsif parsed [ :type ] == "content_block_start" || parsed [ :type ] == "content_block_delta"
63- if @tool_calls . present?
64- result = parsed . dig ( :delta , :partial_json ) . to_s
65- @tool_calls . last . append ( result )
66- else
67- result = parsed . dig ( :delta , :text ) . to_s
68- end
69- elsif parsed [ :type ] == "message_start"
70- @input_tokens = parsed . dig ( :message , :usage , :input_tokens )
71- elsif parsed [ :type ] == "message_delta"
72- @output_tokens =
73- parsed . dig ( :usage , :output_tokens ) || parsed . dig ( :delta , :usage , :output_tokens )
74- elsif parsed [ :type ] == "message_stop"
75- # bedrock has this ...
76- if bedrock_stats = parsed . dig ( "amazon-bedrock-invocationMetrics" . to_sym )
77- @input_tokens = bedrock_stats [ :inputTokenCount ] || @input_tokens
78- @output_tokens = bedrock_stats [ :outputTokenCount ] || @output_tokens
79- end
80- end
81- else
82- content = parsed . dig ( :content )
83- if content . is_a? ( Array )
84- tool_call = content . find { |c | c [ :type ] == "tool_use" }
85- if tool_call
86- @tool_calls << AnthropicToolCall . new ( tool_call [ :name ] , tool_call [ :id ] )
87- @tool_calls . last . append ( tool_call [ :input ] . to_json )
88- else
89- result = parsed . dig ( :content , 0 , :text ) . to_s
74+ content = parsed . dig ( :content )
75+ if content . is_a? ( Array )
76+ result =
77+ content . map do |data |
78+ if data [ :type ] == "tool_use"
79+ call = AnthropicToolCall . new ( data [ :name ] , data [ :id ] )
80+ call . append ( data [ :input ] . to_json )
81+ call . to_tool_call
82+ else
83+ data [ :text ]
84+ end
9085 end
91- end
92-
93- @input_tokens = parsed . dig ( :usage , :input_tokens )
94- @output_tokens = parsed . dig ( :usage , :output_tokens )
9586 end
9687
88+ @input_tokens = parsed . dig ( :usage , :input_tokens )
89+ @output_tokens = parsed . dig ( :usage , :output_tokens )
90+
9791 result
9892 end
9993end
0 commit comments