Skip to content

[chat-headless] Add built-in chat adapters#22479

Open
hasdfa wants to merge 5 commits into
mui:masterfrom
hasdfa:x-chat-headless-adapters
Open

[chat-headless] Add built-in chat adapters#22479
hasdfa wants to merge 5 commits into
mui:masterfrom
hasdfa:x-chat-headless-adapters

Conversation

@hasdfa
Copy link
Copy Markdown
Member

@hasdfa hasdfa commented May 17, 2026

Summary

  • Adds the built-in headless chat adapters: createEchoAdapter and createAiSdkAdapter.
  • Exposes those adapters through the X Chat package surface so consumers and docs can use the supported adapter entry points.
  • Keeps this PR focused on adapter exports and the headless package surface; no Material UI surfaces or docs restructuring are included here.

Details

  • createEchoAdapter provides a local deterministic adapter for simple chat flows, demos, and smoke usage.
  • createAiSdkAdapter bridges the AI SDK integration into the X Chat headless adapter contract.
  • Updates the X Chat exports manifest so the new adapter APIs are part of the generated package exports.

Validation

  • git diff --check upstream/master...x-chat-headless-adapters
  • pnpm --filter "@mui/x-chat-headless" run typescript
  • pnpm test:unit --project "x-chat*" --run

Batch merge structure

Merge top to bottom. #22478 can merge independently, but it should land before #22483 because #22483 assumes the new docs IA.

master
|-- #22478 [docs] Reorganize existing chat docs navigation
`-- #22479 [chat-headless] Add built-in chat adapters
    `-- #22480 [chat-headless] Harden runtime model and message errors
        `-- #22481 [x-chat] Harden material message surfaces
            `-- #22482 [x-chat] Add ChatBox layout slots and migration codemod
                `-- #22483 [docs] Add chat docs page shells (after #22478)
                    `-- #22484 [docs] Add chat playground infrastructure
                        |-- #22485 [docs] Update chat quickstart and backend adapters
                        |-- #22486 [docs] Update chat basics display and behavior
                        |-- #22487 [docs] Update chat customization core material and headless docs
                        `-- #22488 [docs] Update chat overview and component gallery

hasdfa added 2 commits May 17, 2026 20:45
Two built-in adapters that ship with @mui/x-chat-headless: a simple
echo adapter for examples and an AI SDK adapter that bridges Vercel
AI SDK UIMessage streams into ChatMessageChunk events. Wire both
through the headless core barrel so they can be imported from
'@mui/x-chat-headless' and '@mui/x-chat-headless/core'.
@code-infra-dashboard
Copy link
Copy Markdown

code-infra-dashboard Bot commented May 17, 2026

Deploy preview

https://deploy-preview-22479--material-ui-x.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro 0B(0.00%) 0B(0.00%)
@mui/x-charts-premium 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@hasdfa hasdfa added scope: chat Changes related to the AI chat. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. labels May 17, 2026
@hasdfa hasdfa marked this pull request as ready for review May 18, 2026 13:14
Copilot AI review requested due to automatic review settings May 18, 2026 13:14
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 59f5389856

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +116 to +120
const payload = trimmed.startsWith('data:') ? trimmed.slice(5).trimStart() : trimmed;
if (payload === '[DONE]') {
return null;
}
return JSON.parse(payload);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Ignore non-data SSE fields before JSON parsing

parseStreamLine says it should ignore SSE field lines like event: and id:, but the implementation only special-cases data: and otherwise sends the full line to JSON.parse. When an upstream SSE stream includes standard non-data fields (or reconnect metadata), this path throws, and convertToChatStream turns it into a ChatStreamError, terminating an otherwise valid stream.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 849b199. parseStreamLine now ignores non-data SSE field lines such as event:, id:, and retry: before JSON parsing, with SSE coverage added.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds built-in adapter implementations to @mui/x-chat-headless and wires them into the headless/core/adapters entry points, with an additional re-export from @mui/x-chat intended to make quickstart usage easier.

Changes:

  • Added createEchoAdapter (deterministic local adapter) and createAiSdkAdapter (Vercel AI SDK bridge) to @mui/x-chat-headless.
  • Exported the new adapter APIs from @mui/x-chat-headless entry points (src/index.ts, src/core/index.ts, src/adapters/index.ts).
  • Updated @mui/x-chat exports + export manifest to include createEchoAdapter.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
scripts/x-chat.exports.json Adds createEchoAdapter + options to the generated @mui/x-chat export manifest.
packages/x-chat/src/index.ts Re-exports createEchoAdapter from @mui/x-chat-headless for convenience.
packages/x-chat-headless/src/index.ts Exposes new adapters/types from the main headless entry point.
packages/x-chat-headless/src/core/index.ts Exposes new adapters/types from the “core” headless entry point.
packages/x-chat-headless/src/adapters/index.ts Exposes adapters/types from the adapters barrel.
packages/x-chat-headless/src/adapters/createEchoAdapter.ts Introduces the echo adapter implementation.
packages/x-chat-headless/src/adapters/createAiSdkAdapter.ts Introduces the AI SDK adapter implementation (stream + useChat() integration).
packages/x-chat-headless/src/adapters/createAiSdkAdapter.test.ts Adds unit coverage for AI SDK adapter behaviors (object stream, NDJSON/SSE decoding, errors, cancellation, etc.).
Comments suppressed due to low confidence (1)

packages/x-chat-headless/src/adapters/createEchoAdapter.ts:55

  • If signal is already aborted before start() runs, the abort event listener won't fire and the stream won't close until delayMs elapses (it only checks signal.aborted inside the timeout). Consider checking signal.aborted up-front in start() and closing immediately to avoid artificial delay on cancellation.
      return new ReadableStream<ChatMessageChunk>({
        start(controller) {
          const timer = setTimeout(() => {
            if (signal.aborted) {
              controller.close();
              return;
            }
            controller.enqueue({ type: 'start', messageId: replyId });
            controller.enqueue({ type: 'text-start', id: partId });
            controller.enqueue({ type: 'text-delta', id: partId, delta: reply });
            controller.enqueue({ type: 'text-end', id: partId });
            controller.enqueue({ type: 'finish', messageId: replyId, finishReason: 'stop' });
            controller.close();
          }, delayMs);


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/x-chat/src/index.ts Outdated
Comment on lines +221 to +223
// ─── Built-in adapters (re-exported for quickstart convenience) ───────────────
export { createEchoAdapter } from '@mui/x-chat-headless';
export type { CreateEchoAdapterOptions } from '@mui/x-chat-headless';
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 849b199. createAiSdkAdapter and its public structural types are now re-exported from @mui/x-chat, so the package surface matches the PR description.

Comment on lines 129 to +131
{ "name": "ChatVariant", "kind": "TypeAlias" },
{ "name": "createEchoAdapter", "kind": "Function" },
{ "name": "CreateEchoAdapterOptions", "kind": "Interface" },
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 849b199. scripts/x-chat.exports.json now includes createAiSdkAdapter and its public types for the @mui/x-chat export surface.

Comment on lines +41 to +68
return new ReadableStream<ChatMessageChunk>({
start(controller) {
const timer = setTimeout(() => {
if (signal.aborted) {
controller.close();
return;
}
controller.enqueue({ type: 'start', messageId: replyId });
controller.enqueue({ type: 'text-start', id: partId });
controller.enqueue({ type: 'text-delta', id: partId, delta: reply });
controller.enqueue({ type: 'text-end', id: partId });
controller.enqueue({ type: 'finish', messageId: replyId, finishReason: 'stop' });
controller.close();
}, delayMs);

signal.addEventListener(
'abort',
() => {
clearTimeout(timer);
try {
controller.close();
} catch {
// already closed
}
},
{ once: true },
);
},
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 849b199. createEchoAdapter now clears the pending timer on abort/cancel and implements stream cancel() cleanup so it does not enqueue after cancellation.

Comment on lines +23 to +36
/**
* A minimal in-memory `ChatAdapter` that echoes the user's last message.
*
* Intended for quickstart demos, prototyping, and tests — not for production.
* The reply is emitted as a single text chunk after `delayMs` (no simulated
* token-by-token streaming).
*/
export function createEchoAdapter(options: CreateEchoAdapterOptions = {}): ChatAdapter {
const respond = options.respond ?? defaultRespond;
const delayMs = options.delayMs ?? 400;

return {
async sendMessage({ message, signal }) {
const text = message.parts.map((part) => (part.type === 'text' ? part.text : '')).join('');
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 849b199. Added createEchoAdapter unit tests covering delayed emission, abort-before-delay, and stream cancellation cleanup.

Comment on lines +110 to +120
// SSE framing prepends each event with `data: `. Strip it; ignore comments
// (`:` prefix) and SSE field lines we don't care about (`event:`, `id:`, …).
const trimmed = rawLine.trim();
if (trimmed.length === 0 || trimmed.startsWith(':')) {
return null;
}
const payload = trimmed.startsWith('data:') ? trimmed.slice(5).trimStart() : trimmed;
if (payload === '[DONE]') {
return null;
}
return JSON.parse(payload);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 849b199. The SSE parser now skips non-data SSE metadata fields before parsing, and the test stream includes event:, id:, retry:, and comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: chat Changes related to the AI chat. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants