---
title: AI SDK UI
description: Connect agent streams to AI SDK UI clients.
type: guide
summary: Parse UI messages, apply approvals, stream SSE responses, set headers, and rebuild UI history.
---

# AI SDK UI



This adapter provides integration with
[AI SDK UI](https://ai-sdk.dev/docs/ai-sdk-ui), the UI part of the sister
AI SDK in TypeScript.

AI SDK UI provides [`useChat`](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot),
which handles streaming complexity on the client side, such as collecting
message chunks and optimistically updating the UI when the user submits a
new message.

The AI SDK UI's data model is significantly different from the AI SDK for
Python's, which is why you need to convert events and messages back and forth
using the adapter.

## Outbound streaming and message history

Use `to_sse` to convert agent events to AI SDK UI stream chunks:

```python
@app.post("/chat")
async def chat(request: ChatRequest) -> fastapi.responses.StreamingResponse:
    messages, approvals = ai.ui.ai_sdk.to_messages(request.messages)
    ai.ui.ai_sdk.apply_approvals(approvals)

    async def stream_response():
        async with chat_agent.run(model, messages) as stream:
            async for chunk in ai.ui.ai_sdk.to_sse(stream):
                yield chunk

    return fastapi.responses.StreamingResponse(
        stream_response(),
        headers=ai.ui.ai_sdk.UI_MESSAGE_STREAM_HEADERS,
    )
```

Convert stored runtime messages back to AI SDK UI messages for history
endpoints:

```python
@app.get("/chat/{session_id}")
async def get_chat(session_id: str):
    saved = await load_messages(session_id)
    return ai.ui.ai_sdk.to_ui_messages(saved)
```

The adapter groups assistant, tool, and internal hook messages into one
assistant UI message.

Generator tools and subagents emit `PartialToolCallResult` events. The UI
adapter folds those partial values into the corresponding tool output:

```python
@ai.tool
async def draft_mothership_reply(topic: str) -> ai.StreamingTextTool:
    """Draft a reply from the mothership."""
    yield "Checking "
    yield "mothership "
    yield f"records for {topic}."
```

For subagents, `ai.SubAgentTool` streams nested events and stores the nested
transcript as a `MessageBundle`. The model sees the nested agent's final
assistant text.

## Inbound message updates

Accept `UIMessage` values from an AI SDK UI client, then convert them to
runtime messages:

```python
class ChatRequest(pydantic.BaseModel):
    messages: list[ai.ui.ai_sdk.UIMessage]


@app.post("/chat")
async def chat(request: ChatRequest):
    messages, approvals = ai.ui.ai_sdk.to_messages(request.messages)
```

`to_messages` also extracts approval responses from tool parts.

Pass the agent's tools via the optional `tools` argument to re-validate stored
tool results against each tool's declared return type. Without it, tool
results parsed from UI history stay plain JSON:

```python
messages, approvals = ai.ui.ai_sdk.to_messages(
    request.messages, tools=chat_agent.tools
)
```

## Tool approvals

Register extracted approvals before resuming the agent:

```python
messages, approvals = ai.ui.ai_sdk.to_messages(request.messages)
ai.ui.ai_sdk.apply_approvals(approvals)

async with chat_agent.run(model, messages) as stream:
    ...
```

The hook registry stores each approval until the matching tool-approval hook
runs.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)