Capturing tool calls and results while streaming with the Vercel AI SDK

Problem

I’m developing a Slack AI app and trying to implement a debug mode where the end of the Slack message shows details about tool calls and results. I’m unable to stream text and capture the tools after each step or at the end because accessing the tool data consumes the stream.

Code Example

const agentStream = await slackAgent.stream({
  // simulating a user message
  prompt: "What's the weather?",
})

for await (const chunk of agentStream.textStream) {
  // send chunk to slack
  await slackStreamer.append({ markdown_text: chunk })
}

// debug mode: add tool calls and tool results to the end of the slack message
//! This doesn't work because "The tool calls that have been executed in the last step. Automatically consumes the stream."
//? so we somehow need to not only text stream to slack but also capture tool calls right after each step.
const toolCalls = await agentStream.toolCalls
const toolResults = await agentStream.toolResults
await slackStreamer.append({ markdown_text: `Tool calls: ${JSON.stringify(toolCalls)}\nTool results: ${JSON.stringify(toolResults)}` })

await slackStreamer.stop()

// this is also something I'm looking for:
// const allToolCalls = ...
// const allToolResults = ...

I’m trying to capture toolCalls and toolResults right after each step or at the end of the stream without causing the stream to be automatically consumed.

Thank you, hopefully I don’t look too stupid, but I’ve spent hours testing and reading docs by now.

Hey @groundsarts-6607, great to see you here! I’d love to help you out with your question about tool calls during streaming. Could you share a bit more detail on what tools you’re using, along with any specific examples of the issue you’re facing? That’ll help a lot! :slight_smile: Thanks!

Hey, thank you! So far I did a quick single tool from the demo, getWeather

// slack agent outside of main function so it can be used in other files easily
export const slackAgent = new ToolLoopAgent({

  model: openai("gpt-5.1"),

stopWhen: [stepCountIs(20)],

tools: {

getWeather: tool({

description: "Get the weather",

inputSchema: z.object({

city: z.string(),

      }),

execute: async ({ city }) => {

console.log("tool called getWeather", city)

//timeout for 1 seconds

await new Promise((resolve) => setTimeout(resolve, 1000))

console.log("tool promise resolved getWeather")

return {

success: true,

weather: "sunny with a temperature of 23°C",

        }

      },

    }),

  },

})

Hey, quick update, I was able to get this working by doing this, not sure if it’s the best but at least I have both of my questions working

  for await (const part of agentStream.fullStream) {

    if (part.type === "text-delta") {

logger.debug("chunk", { chunk: part.text })

continue

    }




if (part.type === "tool-call" || part.type === "tool-result" || part.type === "tool-error") {

logger.info("tool stream part", part)

    }

  }

 // Option B: after streaming is done, access all steps and final tool calls/results.

  const steps = await agentStream.steps
2 Likes

Great, well done for getting it working @groundsarts-6607 - your solution using fullStream is correct, it’ll give access to all stream events (text, tool calls etc)

To your original post, not stupid at all - asking and sharing here is helpful for others too!

Welcome to the community, and all the best for your project :slight_smile:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.