Skip to content

Feature Request: Native Vercel useChat Integration for fullStream to Support Reasoning Chunks #1

@calebrussel77

Description

@calebrussel77

Overview

First off, thank you for creating agentswarm! It's a powerful library for building multi-agent systems.

I am building a Next.js application using agentswarm on the backend and the Vercel AI SDK's useChat hook on the frontend. While the library is incredibly capable, I've run into an issue regarding the direct integration between agentswarm's streamText output and the data stream expected by useChat, especially when using models that produce reasoning chunks (like Google's Gemini 2.5 Pro).

The core problem is that swarm.streamText() returns a custom result object, and its fullStream of ExtendedTextStreamPart objects is not directly compatible with the Vercel AI SDK's useChat hook without a manual transformation layer.

My Workaround and The Problem

To make this work, I've implemented a custom stream transformer (toDataStreamInternal) that manually converts each ExtendedTextStreamPart from agentswarm into the DataStreamString format that the Vercel AI SDK's createDataStreamResponse helper expects.

Here is my current implementation:

app/api/chat/route.ts

import { createDataStreamResponse } from 'ai'; // Vercel AI SDK Helper
// ... other imports for agentswarm

// This is my handler for POST requests
export async function POST(req: Request) {
  try {
    const { messages } = await req.json();

    // Use the streamText method from the swarm
    const result = swarm.streamText({
      messages,
      experimental_toolCallStreaming: true,
    });

    // Pipe the raw output from agentswarm through our new transformer
    const transformedStream = toDataStreamInternal(result.fullStream, {
      getErrorMessage: (error: unknown) => {
        console.error('[getErrorMessage] error:', error);
        return 'An error occurred.';
      },
      sendReasoning: true, // I want to process reasoning chunks
      sendUsage: true,
      sendSources: false,
      experimental_sendFinish: true,
    });

    // Use the standard Vercel AI SDK helper to create the response
    return createDataStreamResponse({
      status: 200,
      statusText: 'OK',
      execute: dataStream => {
        dataStream.merge(transformedStream);
      },
      onError: (error: unknown) => {
        console.error('[onError] error:', error);
        return 'An error occurred.';
      },
    });
  } catch (error) {
    console.error('Error in AI Agent streaming endpoint:', error);
    return Response.json({ message: 'Internal server error' }, { status: 500 });
  }
}

// Custom transformer to bridge agentswarm's stream with Vercel's stream protocol
function toDataStreamInternal(
  fullStream: ReadableStream<any>,
  {
    // ... options
  }: {
    // ... options
  }
): ReadableStream<DataStreamString> {
  return fullStream.pipeThrough(
    new TransformStream<ExtendedTextStreamPart<any>, DataStreamString>({
      transform: async (chunk, controller) => {
        const chunkType = chunk.type;
        // This switch handles all the different chunk types from agentswarm
        switch (chunkType) {
          case 'text-delta': {
            controller.enqueue(formatDataStreamPart('text', chunk.textDelta));
            break;
          }
          // This case specifically handles the reasoning chunk
          case 'reasoning': {
            if (sendReasoning) {
              controller.enqueue(
                formatDataStreamPart('reasoning', chunk.textDelta)
              );
            }
            break;
          }
          // ... other cases for tool calls, finish, etc.
          case 'tool-call': {
             controller.enqueue(formatDataStreamPart('tool_call', { /*...*/ }));
             break;
          }
          // ...
          default: {
            const exhaustiveCheck: never = chunkType;
            throw new Error(`Unknown chunk type: ${exhaustiveCheck}`);
          }
        }
      },
    })
  );
}

This workaround functions correctly for standard text and tool calls.

However, when using a model that produces reasoning chunks (e.g., Gemini 2.5 Pro), the stream fails with the following error logged from the onError callback of createDataStreamResponse:

[onError] error [Error: Unhandled chunk type: reasoning]

Root Cause Analysis

The error message Unhandled chunk type: reasoning strongly suggests that the issue lies within the Vercel AI SDK's server-side helpers (createDataStreamResponse and its internal stream merging logic).

My custom transformer, toDataStreamInternal, correctly identifies the { type: 'reasoning', ... } chunk from agentswarm's fullStream and attempts to enqueue it as a 'reasoning' data part for the client. However, it appears the standard Vercel AI SDK server helpers do not natively recognize or know how to process this 'reasoning' data part type, causing them to throw the "Unhandled chunk type" error.

This means that while my transformer is doing its job, the underlying Vercel helper can't handle the data it's being given.

Suggested Solution & Feature Request

Manually maintaining a transformer like the one above is complex and brittle. As agentswarm and the Vercel AI SDK evolve, this transformer will need constant updates to handle new chunk types and potential breaking changes.

The ideal solution would be for agentswarm to provide a native helper function that correctly transforms its fullStream into a Response object that is fully compatible with useChat.

This would significantly improve the developer experience and make the library more robust. A hypothetical helper could look something like this:

// Desired API
export async function POST(req: Request) {
  const { messages } = await req.json();

  const swarmResult = swarm.streamText({
    messages,
    experimental_toolCallStreaming: true,
  });

  // This helper would handle all the transformation logic internally
  return swarmResult.toDataStreamResponse({
      sendReasoning: true, // allow options to be passed
  });
}

This is similar to how the base Vercel AI SDK's streamText works and would make agentswarm a seamless drop-in for developers already familiar with that ecosystem.

Thank you for considering this feature request. I believe it would make an already great library even better.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions