Skip to content

Tool call events are buffered until stream ends (flush) causing perceived streaming delay #413

@metaslim

Description

@metaslim

Description

When using @openrouter/ai-sdk-provider with multi-step tool calling (e.g., via Vercel AI SDK's streamText + stepCountIs), the streaming response appears buffered to the end user. Text deltas stream fine, but something in the tool-call streaming path causes a perceived delay before the tool executes and the next LLM step begins.

Switching to @ai-sdk/openai with baseURL: 'https://openrouter.ai/api/v1' (same API, different SDK client) resolves the issue — streaming becomes real-time token-by-token.

Bug Found: Missing tool-input-end in multi-chunk tool call path

In src/chat/index.ts, the single-chunk tool call path (lines ~905-949) correctly emits the full sequence:

  • tool-input-starttool-input-deltatool-input-endtool-call

But the multi-chunk merge path (lines ~990-1013) skips tool-input-end:

  • tool-input-deltatool-call (missing tool-input-end before tool-call)
// Multi-chunk path — emits tool-call but NO tool-input-end before it
if (isParsableJson(toolCall.function.arguments)) {
  // ❌ Missing: controller.enqueue({ type: 'tool-input-end', id: toolCall.id });
  controller.enqueue({
    type: 'tool-call',
    toolCallId: toolCall.id ?? generateId(),
    toolName: toolCall.function.name,
    input: toolCall.function.arguments,
    ...
  });
  toolCall.sent = true;
}

This may cause downstream consumers (like the AI SDK's stream processor) to wait for the missing tool-input-end event, contributing to the perceived buffering.

Reproduction

  1. Use @openrouter/ai-sdk-provider v2.x with ai SDK v6.x
  2. Configure streamText() with tools and stepCountIs(n) for multi-step
  3. Send a message that triggers a tool call where arguments arrive across multiple chunks
  4. Observe that streaming feels delayed/buffered compared to @ai-sdk/openai

Workaround

Use @ai-sdk/openai with OpenRouter's base URL instead:

import { createOpenAI } from '@ai-sdk/openai';

const openrouterProvider = createOpenAI({
  apiKey: process.env.OPENROUTER_API_KEY,
  baseURL: 'https://openrouter.ai/api/v1',
});

// Force chat completions API (not Responses API)
export const openrouter = (modelId: string) => openrouterProvider.chat(modelId);

Environment

  • `@openrouter/ai-sdk-provider`: 2.2.3
  • `ai`: 6.0.81
  • Node.js: 24
  • Model tested: `openai/gpt-5.2`

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions