Skip to content

feat(agents): add stream response conversion methods#21

Merged
zrosenbauer merged 5 commits intomainfrom
feat/agents-stream-response-methods
Mar 18, 2026
Merged

feat(agents): add stream response conversion methods#21
zrosenbauer merged 5 commits intomainfrom
feat/agents-stream-response-methods

Conversation

@zrosenbauer
Copy link
Member

Summary

  • Adds toTextStreamResponse() and toUIMessageStreamResponse() to StreamResult, delegating to the underlying AI SDK streamText result for direct HTTP response conversion (Hono, Express, Bun)
  • Flow agents throw a descriptive error since they lack a single model stream to delegate to
  • Re-exports UIMessage and UIMessageStreamOptions from ai for consumer type access
  • Includes changeset for minor version bump

Test plan

  • Typecheck passes (pnpm typecheck --filter=@funkai/agents)
  • Build passes (pnpm build --filter=@funkai/agents)
  • All 527 tests pass (pnpm test --filter=@funkai/agents)
  • 3 new tests verify delegation behavior for both methods

Add `toTextStreamResponse()` and `toUIMessageStreamResponse()` to
`StreamResult`, delegating to the underlying AI SDK `streamText` result.
This enables direct HTTP response conversion for API frameworks like
Hono, Express, and Bun.

Flow agents throw a descriptive error since they build streams from
step execution rather than a single `streamText` call.

Re-exports `UIMessage` and `UIMessageStreamOptions` from `ai` for
consumer type access.

Co-Authored-By: Claude <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 18, 2026

🦋 Changeset detected

Latest commit: e997e1c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@funkai/agents Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Mar 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
funkai Ignored Ignored Preview Mar 18, 2026 2:37am

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8b4798e3-bc0a-481c-b0c1-0f684bdf7678

📥 Commits

Reviewing files that changed from the base of the PR and between 6c36af3 and e997e1c.

📒 Files selected for processing (4)
  • packages/agents/src/core/agents/base/agent.ts
  • packages/agents/src/core/agents/flow/flow-agent.ts
  • packages/agents/src/core/agents/flow/stream-response.test.ts
  • packages/agents/src/core/agents/flow/stream-response.ts

📝 Walkthrough

Walkthrough

Adds two response helper methods, toTextStreamResponse() and toUIMessageStreamResponse(), to StreamResult. Regular agents delegate to the AI SDK; Flow agents use a new buildStreamResponseMethods factory to convert ReadableStream into HTTP Responses.

Changes

Cohort / File(s) Summary
Type definitions & exports
packages/agents/src/core/agents/base/types.ts, packages/agents/src/index.ts
Extended StreamResult with toTextStreamResponse and toUIMessageStreamResponse. Re-exported UIMessage and UIMessageStreamOptions.
Regular agent wiring & tests
packages/agents/src/core/agents/base/agent.ts, packages/agents/src/core/agents/base/agent.test.ts
StreamResult now delegates response helpers to underlying AI SDK result. Added tests covering delegation and init preservation.
Flow: stream-response implementation & tests
packages/agents/src/core/agents/flow/stream-response.ts, packages/agents/src/core/agents/flow/stream-response.test.ts
New factory buildStreamResponseMethods(getStream) producing toTextStreamResponse (text/plain transform) and toUIMessageStreamResponse (UI message SSE-like stream). Comprehensive tests for headers, empty bodies, and streaming behavior.
Flow agent integration & tests
packages/agents/src/core/agents/flow/flow-agent.ts, packages/agents/src/core/agents/flow/flow-agent.test.ts
Wired buildStreamResponseMethods into flowAgent stream result and exposed the new methods; added tests validating response headers, custom init preservation, and stream consumption.
Changelog
.changeset/stream-response-methods.md
Added changelog entry documenting the two new stream response helpers and bumped package entry for @funkai/agents.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant FlowAgent
  participant StreamParts as ReadableStream<StreamPart>
  participant Transformer as TransformStream/TextEncoder
  participant UIStream as createUIMessageStream
  participant HTTP as Response

  Client->>FlowAgent: call agent.stream()
  FlowAgent->>StreamParts: provide readable (via getStream)
  Client->>FlowAgent: call toTextStreamResponse()/toUIMessageStreamResponse()
  FlowAgent->>StreamParts: obtain stream (lazy)
  alt toTextStreamResponse
    StreamParts->>Transformer: filter text-delta events
    Transformer->>HTTP: produce text/plain Response body
  else toUIMessageStreamResponse
    StreamParts->>UIStream: create UI message stream
    UIStream->>HTTP: produce SSE-like Response body
  end
  FlowAgent->>Client: return Response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding stream response conversion methods (toTextStreamResponse and toUIMessageStreamResponse) to StreamResult.
Description check ✅ Passed The description clearly relates to the changeset, detailing the new methods, re-exports, error handling for Flow agents, and verification of the test plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Replace error-throwing stubs with working implementations using a
shared `buildStreamResponseMethods` utility. Flow agents now build
`toTextStreamResponse` by filtering text-delta events from the stream
and `toUIMessageStreamResponse` via the AI SDK's `createUIMessageStream`
and `createUIMessageStreamResponse` utilities.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/stream-response-methods.md:
- Line 5: The changeset and docs are incorrect: update the changeset text to
state that StreamResult exposes toTextStreamResponse() and
toUIMessageStreamResponse() (delegating to the underlying Vercel AI SDK
streamText result) and that flow agents also support these methods (remove the
claim about throwing an error); then update the streaming documentation to add
entries for StreamResult.toTextStreamResponse,
StreamResult.toUIMessageStreamResponse and the re-exported
UIMessageStreamOptions type, showing usage examples for base agents vs flow
agents and clarifying any behavioral differences; ensure the public API
re-export of UIMessageStreamOptions is documented along with the method
signatures.

In `@packages/agents/src/core/agents/base/agent.ts`:
- Around line 539-540: The helpers toTextStreamResponse and
toUIMessageStreamResponse currently delegate to aiResult which consumes
aiResult.fullStream that's already being read by the background consumer,
causing double-consumption errors; fix by tee()-ing fullStream before any
consumption: call aiResult.fullStream.tee() and use one branch for the
background task (replace the background consumer to read that branch) and return
the other branch (or create new tees per helper call) from
toTextStreamResponse/toUIMessageStreamResponse so each consumer gets its own
readable stream branch; alternatively remove the delegations and document that
callers must obtain their own tee branch from aiResult.fullStream.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f1cdaa35-9220-475b-82c0-3a1d037273eb

📥 Commits

Reviewing files that changed from the base of the PR and between c702ef1 and 09d5dc6.

📒 Files selected for processing (6)
  • .changeset/stream-response-methods.md
  • packages/agents/src/core/agents/base/agent.test.ts
  • packages/agents/src/core/agents/base/agent.ts
  • packages/agents/src/core/agents/base/types.ts
  • packages/agents/src/core/agents/flow/flow-agent.ts
  • packages/agents/src/index.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/agents/src/core/agents/base/stream-response.ts`:
- Around line 21-34: The JSDoc for the exported function
buildStreamResponseMethods is missing an `@example`; update the block above
buildStreamResponseMethods to include a concise `@example` that demonstrates
calling the thunk getStream and consuming one of the returned methods (e.g.,
await response.toTextStreamResponse() or await
response.toUIMessageStreamResponse()), showing that only one of fullStream,
toTextStreamResponse, or toUIMessageStreamResponse should be consumed; reference
the symbols getStream, fullStream, toTextStreamResponse, and
toUIMessageStreamResponse in the example so the usage is clear.
- Around line 51-56: toTextStreamResponse currently spreads init?.headers into
the Response headers which fails when headers is a Headers instance or tuple
array; update to normalize and merge via the Headers API instead: create a
Headers object from init?.headers (e.g. new Headers(init?.headers)), set or
overwrite the "Content-Type" header to "text/plain; charset=utf-8" on that
Headers instance, then pass that Headers instance as the Response headers when
constructing the new Response(textStream, { ...init, headers }). Ensure you
reference the toTextStreamResponse function, the textStream and init variables,
and replace the object-spread merge with this Headers-based merge to preserve
all header types.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c1eda86b-0c33-4f73-9b77-a78d68ad409d

📥 Commits

Reviewing files that changed from the base of the PR and between 09d5dc6 and 752dae1.

📒 Files selected for processing (2)
  • packages/agents/src/core/agents/base/stream-response.ts
  • packages/agents/src/core/agents/flow/flow-agent.ts

…ests

Move `buildStreamResponseMethods` from base to flow directory since
only flow agents use it (base agents delegate to aiResult directly).

Add unit tests for the utility (stream-response.test.ts) covering
text stream filtering, content-type headers, custom init, and UI
message stream response creation.

Add integration tests in flow-agent.test.ts verifying both response
methods work end-to-end through the flow agent's stream() path.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/agents/src/core/agents/flow/stream-response.ts`:
- Around line 67-73: The identity TransformStream wrapping `source` (created as
`uiStream` via new TransformStream<StreamPart>({...})`) is unnecessary—remove
the `uiStream` variable and the TransformStream creation and pass `source`
directly into `writer.merge(...)` (or wherever `uiStream` is consumed). Update
any references from `uiStream` to `source` and delete the redundant
TransformStream/transform function to avoid the extra passthrough overhead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e36cca5f-4e78-4956-9b56-b3b2b5d56a77

📥 Commits

Reviewing files that changed from the base of the PR and between 752dae1 and 6c36af3.

📒 Files selected for processing (4)
  • packages/agents/src/core/agents/flow/flow-agent.test.ts
  • packages/agents/src/core/agents/flow/flow-agent.ts
  • packages/agents/src/core/agents/flow/stream-response.test.ts
  • packages/agents/src/core/agents/flow/stream-response.ts

zrosenbauer and others added 2 commits March 17, 2026 22:35
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comment explaining AI SDK internal tee() safety for base agent
  response delegation (thread PRRT_kwDORkqTG851Brsl)
- Add @example to buildStreamResponseMethods JSDoc
  (thread PRRT_kwDORkqTG851BuQt)
- Fix unsafe header merge using Headers API instead of object spread,
  respect caller-provided Content-Type (thread PRRT_kwDORkqTG851BuQv)
- Remove unnecessary identity TransformStream in toUIMessageStreamResponse,
  pass source directly to writer.merge (thread PRRT_kwDORkqTG851BwbT)
- Add tests for Headers instance input and Content-Type preservation

Co-Authored-By: Claude <noreply@anthropic.com>
@zrosenbauer zrosenbauer merged commit a7c3354 into main Mar 18, 2026
5 checks passed
@zrosenbauer zrosenbauer deleted the feat/agents-stream-response-methods branch March 18, 2026 05:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant