@@ -27,20 +27,14 @@ def initialize(bot, strategy, persist_summaries: true)
2727 def summarize ( user , &on_partial_blk )
2828 truncated_content = content_to_summarize . map { |cts | truncate ( cts ) }
2929
30- summary = fold ( truncated_content , user , & on_partial_blk )
31-
32- clean_summary = Nokogiri :: HTML5 . fragment ( summary ) . css ( "ai" ) &. first &. text || summary
30+ # Done here to cover non-streaming mode.
31+ json_reply_end = " \" }"
32+ summary = fold ( truncated_content , user , & on_partial_blk ) . chomp ( json_reply_end )
3333
3434 if persist_summaries
35- AiSummary . store! (
36- strategy ,
37- llm_model ,
38- clean_summary ,
39- truncated_content ,
40- human : user &.human? ,
41- )
35+ AiSummary . store! ( strategy , llm_model , summary , truncated_content , human : user &.human? )
4236 else
43- AiSummary . new ( summarized_text : clean_summary )
37+ AiSummary . new ( summarized_text : summary )
4438 end
4539 end
4640
@@ -118,17 +112,40 @@ def fold(items, user, &on_partial_blk)
118112 )
119113
120114 summary = +""
115+ # Auxiliary variables to get the summary content from the JSON response.
116+ raw_buffer = +""
117+ json_start_found = false
118+ json_reply_start_regex = /\{ \s *"summary"\s *:\s *"/
119+ unescape_regex = %r{\\ (["/bfnrt])}
120+ json_reply_end = "\" }"
121+
121122 buffer_blk =
122- Proc . new do |partial , cancel , placeholder , type |
123+ Proc . new do |partial , cancel , _ , type |
123124 if type . blank?
124- summary << partial
125- on_partial_blk . call ( partial , cancel ) if on_partial_blk
125+ if json_start_found
126+ unescaped_partial = partial . gsub ( unescape_regex , '\1' )
127+ summary << unescaped_partial
128+
129+ on_partial_blk . call ( partial , cancel ) if on_partial_blk
130+ else
131+ raw_buffer << partial
132+
133+ if raw_buffer . match? ( json_reply_start_regex )
134+ buffered_start =
135+ raw_buffer . gsub ( json_reply_start_regex , "" ) . gsub ( unescape_regex , '\1' )
136+ summary << buffered_start
137+
138+ on_partial_blk . call ( buffered_start , cancel ) if on_partial_blk
139+
140+ json_start_found = true
141+ end
142+ end
126143 end
127144 end
128145
129- bot . reply ( context , &buffer_blk )
146+ bot . reply ( context , llm_args : { extra_model_params : response_format } , &buffer_blk )
130147
131- summary
148+ summary . chomp ( json_reply_end )
132149 end
133150
134151 def available_tokens
@@ -155,10 +172,26 @@ def truncate(item)
155172 item
156173 end
157174
158- def text_only_update ( &on_partial_blk )
159- Proc . new do |partial , cancel , placeholder , type |
160- on_partial_blk . call ( partial , cancel ) if type . blank?
161- end
175+ def response_format
176+ {
177+ response_format : {
178+ type : "json_schema" ,
179+ json_schema : {
180+ name : "reply" ,
181+ schema : {
182+ type : "object" ,
183+ properties : {
184+ summary : {
185+ type : "string" ,
186+ } ,
187+ } ,
188+ required : [ "summary" ] ,
189+ additionalProperties : false ,
190+ } ,
191+ strict : true ,
192+ } ,
193+ } ,
194+ }
162195 end
163196 end
164197 end
0 commit comments