Use when connecting AI SDK for Python streams to AI SDK UI useChat clients.
npx skills add https://github.com/vercel-labs/seal --skill ai-python-ui-adapter
Frontend:
const chat = useChat({
transport: new DefaultChatTransport({ api: "/api/chat" }),
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
});
Use chat.sendMessage(...) to send user input. Use
chat.addToolApprovalResponse(...) from approval buttons.
Backend request:
class ChatRequest(pydantic.BaseModel):
messages: list[ai.agents.ui.ai_sdk.UIMessage]
messages, approvals = ai.agents.ui.ai_sdk.to_messages(request.messages)
ai.agents.ui.ai_sdk.apply_approvals(approvals)
Backend stream:
async def body():
async with agent.run(model, messages) as stream:
async def events():
async for event in stream:
if (
isinstance(event, ai.events.HookEvent)
and event.hook.status == "pending"
):
ai.defer_hook(event.hook)
yield event
async for chunk in ai.agents.ui.ai_sdk.to_sse(events()):
yield chunk
return StreamingResponse(
body(),
headers=ai.agents.ui.ai_sdk.UI_MESSAGE_STREAM_HEADERS,
)
The adapter handles UIMessage parsing, message IDs, tool state, approvals,
subagent MessageBundle values, and AI SDK UI stream events.
You handle the HTTP route, auth, storage, session lookup, frontend rendering,
and when to defer hooks.
For saved UI history, use:
ui_messages = ai.agents.ui.ai_sdk.to_ui_messages(messages)
Take vercel-labs/ai-python-ui-adapter from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.