@@ -118,6 +118,19 @@ RagnarChat <- R6::R6Class(
118118 )
119119 },
120120
121+ stream_async = function (... , tool_mode = c(" concurrent" , " sequential" ), stream = c(" text" , " content" )) {
122+ result <- private $ callback_user_turn(... )
123+ do.call(
124+ super $ stream_async ,
125+ append(result , list (tool_mode = tool_mode , stream = stream ))
126+ )
127+ },
128+
129+ stream = function (... , stream = c(" text" , " content" )) {
130+ result <- private $ callback_user_turn(... )
131+ do.call(super $ stream , append(result , list (stream = stream )))
132+ },
133+
121134 # ' @field ragnar_tool A function that retrieves relevant chunks from the store.
122135 # ' This is the function that is registered as a tool in the chat.
123136 ragnar_tool = function (query ) {
@@ -413,6 +426,34 @@ RagnarChat <- R6::R6Class(
413426 ... ,
414427 ContentRagnarDocuments(text = documents )
415428 )
429+ },
430+
431+ # ' @description
432+ # ' Summarizes the tools calls in the chat history. The assistant tool
433+ # ' call request is redacted, and the chunks are summarized.
434+ # '
435+ # ' The ellmer::ToolCallRequest becomes a `ellmer::ContentText` with:
436+ # ' ```
437+ # ' <Redacted by summarization tool: query={}>
438+ # ' ```
439+ # '
440+ # ' The `ellmer::ContentToolResult` becomes a `ellmer::ContentText` with
441+ # ' a summary of the chunks given by the `summarize_chunks` callback.
442+ # ' `summarize_chunks` takes a list of `chunks` as argument.
443+ # '
444+ turns_summarize_tool_calls = function (summarize_chunks ) {
445+ turns <- self $ get_turns() | >
446+ turns_modify_tool_calls(function (assistant_turn , user_turn ) {
447+ summarize_tool_call(
448+ assistant_turn = assistant_turn ,
449+ user_turn = user_turn ,
450+ summarize_chunks ,
451+ tool_name = self $ ragnar_tool_def @ name
452+ )
453+ },
454+ tool_name = self $ ragnar_tool_def @ name
455+ )
456+ self $ set_turns(turns )
416457 }
417458 ),
418459
@@ -457,3 +498,60 @@ content_set_chunks <- function(x, chunks) {
457498 }
458499 x
459500}
501+
502+ # ' Applies a function to pairs of turns that represent tool calls.
503+ # ' Otherwise keep them unchanged.
504+ # ' @noRd
505+ turns_modify_tool_calls <- function (turns , fn , tool_name = NULL ) {
506+ i <- 1
507+ while (i < length(turns )) {
508+ if (turns [[i ]]@ role == " user" ) {
509+ i <- i + 1 ; next
510+ };
511+ if (! is_tool_call(turns [[i ]], tool_name )) {
512+ i <- i + 1 ; next ;
513+ }
514+
515+ turns [c(i , i + 1 )] <- fn(turns [[i ]], turns [[i + 1 ]])
516+ i <- i + 2
517+ }
518+ turns
519+ }
520+
521+ is_tool_call <- function (x , tool_name = NULL ) {
522+ for (content in x @ contents ) {
523+ if (S7 :: S7_inherits(content , ellmer :: ContentToolRequest )) {
524+ if (length(x @ contents ) > 1 ) {
525+ cli :: cli_warn(
526+ " Tool call request found in turn with multiple contents, this may lead to unexpected results."
527+ )
528+ }
529+ if (! is.null(tool_name ) && content @ tool @ name != tool_name ) {
530+ return (FALSE )
531+ }
532+
533+ return (TRUE )
534+ }
535+ }
536+ FALSE
537+ }
538+
539+ summarize_tool_call <- function (assistant_turn , user_turn , summarize , tool_name ) {
540+ assistant_turn @ contents <- list (
541+ ellmer :: ContentText(
542+ text = paste0(
543+ " <Redacted by summarization tool: query=" ,
544+ user_turn @ contents [[1 ]]@ request @ arguments $ query , " >"
545+ )
546+ )
547+ )
548+
549+ summary <- lapply(user_turn @ contents , \(x ) content_get_chunks(x , tool_name )) | >
550+ unlist(recursive = FALSE ) | >
551+ summarize()
552+
553+ user_turn @ contents <- list (ellmer :: ContentText(text = summary ))
554+
555+ list (assistant_turn , user_turn )
556+ }
557+
0 commit comments