posit-dev / posit-dev/shinychat
Subagent token streams
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 139
- Forks
- 28
- Avg merge
- 23h 44m
- Merged PRs (30d)
- 50
Description
I'm building a multi-agent app based on ellmer chat objects, and I'm looking for a way to expose subagent token streams to the user. In my case, subagents (and subagents of those subagents) do most of the work, so it would meaningfully improve the user experience to expose the subagent streams, sub-subagent streams, sub-sub-subagent streams, etc.
My chat objects call one another as tools, as in https://github.com/tidyverse/ellmer/issues/821#issuecomment-5516269236. Reprex from that post:
orchestrator <- ellmer::chat_anthropic(
system_prompt = "You are an orchestrator agent.",
echo = "output"
)
subagent <- ellmer::chat_anthropic(
system_prompt = "You are the subagent with test phrase 'Recognition code 927, I am a potato'.",
echo = "output"
)
ellmer::tool(
name = "subagent1",
description = "Prompt subagent.",
fun = function(prompt) {
subagent$chat_async(prompt, tool_mode = "concurrent")
},
arguments = list(
prompt = ellmer::type_string(
description = "Prompt to send to the subagent."
)
)
) |>
orchestrator$register_tool()
orchestrator$chat_async(
"Ask the subagent for its test phrase.",
tool_mode = "concurrent"
)
# After a few seconds...
orchestrator$get_turns()
#> [[1]]
#> <Turn: user>
#> Ask the subagent for its test phrase.
#>
#> [[2]]
#> <Turn: assistant>
#> [tool request (toolu_bdrk_01U7sPgkR8zpGdvhwDxyVRiE)]: subagent(prompt = "What is your test phrase?")
#>
#> [[3]]
#> <Turn: user>
#> [tool result (toolu_bdrk_01U7sPgkR8zpGdvhwDxyVRiE)]: Recognition code 927, I am a potato
#>
#> [[4]]
#> <Turn: assistant>
#> The subagent's test phrase is: **"Recognition code 927, I am a potato"**
As a naive shinychat app, my example looks like this. In the general case, the subagents could have subagents of their own, and I'd want to see their token streams too. But just for now, let's say the subagents only go one level deep.
new_chat <- function() {
orchestrator <- ellmer::chat_anthropic(
system_prompt = "You are an orchestrator agent.",
echo = "output"
)
subagent1 <- ellmer::chat_anthropic(
system_prompt = "You are subagent 1.",
echo = "output"
)
subagent2 <- ellmer::chat_anthropic(
system_prompt = "You are subagent 2 with test phrase 'Recognition code 1157, I am an onion.'",
echo = "output"
)
ellmer::tool(
name = "test_phrase",
description = "Get the test phrase",
fun = function() {
for (i in seq_len(10)) {
message(i)
Sys.sleep(1)
}
'Recognition code 927, I am a potato.'
}
) |>
subagent1$register_tool()
ellmer::tool(
name = "test_phrase",
description = "Get the test phrase",
fun = function() {
for (i in seq_len(10)) {
message(i)
Sys.sleep(1)
}
'Recognition code 1157, I am an onion.'
}
) |>
subagent2$register_tool()
ellmer::tool(
name = "subagent",
description = "Prompt a subagent.",
fun = function(name, prompt) {
if (name == "subagent1") {
subagent1$chat_async(prompt, tool_mode = "concurrent")
} else if (name == "subagent2") {
subagent2$chat_async(prompt, tool_mode = "concurrent")
}
},
arguments = list(
name = ellmer::type_enum(
description = "Name of the sub-agent to prompt.",
values = c("subagent1", "subagent2")
),
prompt = ellmer::type_string(
description = "Prompt to send to the sub-agent."
)
)
) |>
orchestrator$register_tool()
orchestrator
}
ui <- bslib::page_fillable(
title = "Example multi-agent workflow",
shinychat::chat_ui(
"chat",
height = "100%",
greeting = "Enter 'Ask all subagents for their test phrases.'"
)
)
server <- function(input, output, session) {
chat <- new_chat()
shiny::observeEvent(input$chat_user_input, {
stream <- chat$stream_async(input$chat_user_input, stream = "content")
shinychat::chat_append("chat", stream)
})
}
shiny::shinyApp(ui, server)
When I run it an ask the subagents for their test phrases, I see nice tool-calling blocks appear, but those blocks don't show anything about what those subagents are doing.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with new_chat(), orchestrator$register_tool(), chat$stream_async(), and shinychat::chat_append() in the reprex. Trace how nested ellmer chat_async calls produce tool output and determine how one-level subagent token events could reach the UI. Done means the example visibly exposes subagent streams while preserving the existing tool-calling display.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- r, typescript
- Domain
- frontend, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100