EnxDev opened a new issue, #41059:
URL: https://github.com/apache/superset/issues/41059
## [SIP-214] Introducing a Chat extension in Superset
### 1. Introduction
This SIP proposes a new extension point that enables third-party chat
integrations to be embedded directly into the Superset user interface through
the existing extension framework.
The goal is to provide a stable, supported mechanism for chat providers to
integrate with Superset without requiring direct access to internal application
state, Redux stores, or implementation-specific frontend modules.
Chat extensions should interact with Superset through the same
extension-oriented principles already established for other extension surfaces,
such as SQL Lab.
The proposal focuses on two core concerns:
- Defining how chat extensions are registered and rendered.
- Defining how chat extensions receive contextual information about the
currently active application surface.
This SIP intentionally does not prescribe any specific chat implementation,
user experience, LLM provider, or backend architecture.
<details>
<summary>1.1 Motivation</summary>
AI-powered assistants are becoming a common way for users to interact with
analytical applications. Superset should provide a standardized extension
mechanism that allows community-built chat integrations to participate in the
platform without depending on internal frontend implementation details.
Today, chat integrations must either be embedded through custom application
modifications or rely on unsupported access to internal application state. Both
approaches create maintenance challenges and make integrations fragile when the
architecture evolves.
#### This SIP introduces a stable extension contract that:
- Enables chat integrations to be distributed as standard Superset
extensions.
- Preserves separation between host and extension responsibilities.
- Allows chat implementations to access contextual information about the
current page and entity being viewed.
- Keeps authorization and permission enforcement aligned with existing
Superset APIs.
- Remains compatible with future architecture changes.
</details>
<details>
<summary>1.2 Goals</summary>
#### The goals of this SIP are:
Introduce a dedicated chat extension point within the Superset application.
Provide chat extensions with host-managed, permission-aligned context.
Establish stable extension-facing APIs for dashboard, explore, dataset, and
navigation context.
Maintain isolation between chat implementations and Superset internals.
Preserve compatibility with future extension capabilities and AI-related
initiatives.
</details>
<details>
<summary> 1.3 Out of Scope </summary>
The following capabilities are explicitly out of scope for this SIP.
Client Actions and Agentic UI Manipulation
This SIP defines how chat extensions are mounted and how they receive
context from the host application.
It does not define how a chat performs actions within the user interface,
such as:
- Modifying chart configuration.
- Updating dashboard layouts.
- Editing SQL queries.
- Triggering frontend workflows.
These capabilities are deferred to the proposed Client Actions SIP.
Chat User Experience
The chat user interface remains entirely owned by the extension.
#### This SIP does not prescribe:
- Visual design.
- Conversation experience.
- Streaming behavior.
- Message persistence.
- Prompting strategy.
- Accessibility implementation details.
- Branding or styling.
#### LLM and Backend Infrastructure
The following concerns remain extension-specific:
- Model providers.
- MCP implementations.
- Agent frameworks.
- Tool execution systems.
- Prompt orchestration.
- Backend services.
Superset acts only as the host application and context provider.
User Preferences
Per-user chat preferences are considered an important future capability but
are intentionally out of scope for this SIP.
</details>
### 2. Requirements
<details>
<summary>2.1 Functional Requirements</summary>
#### Registration and Rendering
The platform must allow extensions to register the Chat providers through
the standard extension system.
The host must:
- Support registration of chat extensions.
- Render a chat UI contributed by an extension.
- Maintain a single active chat instance at any given time.
- Make the chat available across supported application surfaces.
- Support fully custom chat user interfaces.
#### Context Sharing
Context is obtained through the same set of APIs used by other extensions.
These APIs are organized by namespaces and provide the methods and events
that will be used by the chat to collect application state.
- Extensions must not be required to:
- Read Redux state.
- Access internal application modules.
- Depend on component-level implementation details.
- Reconstruct semantic context from frontend internals.
Instead, extensions consume stable namespace APIs provided by the host.
#### Conversation State
The conversation state remains entirely owned by the chat extension.
This includes:
- Message history.
- Tool execution state.
- Streaming buffers.
- Conversation persistence.
- Session management.
The host is responsible only for exposing contextual information.
</summary>
</details>
<details>
<summary>2.2 Non-Functional Requirements</summary>
Security and Authorization
Context shared with chat extensions must remain aligned with Superset's
existing authorization model.
The host must not expose:
- Entities the current user cannot access.
- Metadata outside the user's permission scope.
- Datasource-derived information unavailable through existing APIs.
The extension-facing APIs defined by this SIP operate on data that has
already been scoped to the current user.
#### Fault Isolation
Failures within chat extensions must not affect the stability of the host
application.
Errors originating from third-party chat implementations should be isolated
to the chat mount boundary.
#### Extensibility
The architecture should support future:
• Application surfaces.
• AI-related capabilities.
• Extension APIs.
• Context providers.
without requiring redesign of the chat extension model.
#### Vendor Neutrality
The architecture must remain independent of any specific:
• LLM provider.
• AI platform.
• Agent framework.
• Backend implementation.
</details>
### 3. Default resolution and first-run behavior
<details>
<summary>3.1 Selection logic</summary>
The host resolves the active chat deterministically using the following
policy:
- No chat extension is registered. No chat is rendered and the chat surface
remains empty. This is not an error state; the host simply has nothing to mount.
- When multiple chat extensions are registered, the host should resolve to
the last one registered.
- The host should clearly state in the server logs what's the selected chat
extension alongside the ignored ones when multiple extensions are provided.
</details>
### 4. Proposed New Contribution Type
<details>
<summary>4.1 Overview</summary>
This SIP introduces a new contribution type that allows chat providers to
integrate directly into the Superset application.
<br>
| Contribution Type | Registration API | Cardinality |
|------------------|----------------------|-------------|
| `core.chat` | `chat.registerChat()` | Singleton |
</details>
<details>
<summary>4.2 Singleton Rendering Model </summary>
Unlike most contribution types, which allow multiple contributions to be
rendered simultaneously, the chat is intentionally exclusive and renders a
single active provider.
#### Motivation
Chat interactions are inherently conversational and user-focused. Rendering
multiple chat providers simultaneously would:
- Create competing user experiences.
- Introduce ambiguity regarding which chat should respond.
- Increase UI complexity.
- Reduce discoverability.
For these reasons, chat rendering is treated as a deployment-level selection
rather than a multi-provider composition model.
</details>
<details>
<summary>4.3 Chat Lifecycle</summary>
Host Responsibilities
The host is responsible for:
• Providing the chat mount point.
• Resolving the active chat provider.
• Loading chat extensions.
• Managing chat lifecycle integration.
• Maintaining fault isolation boundaries.
• Preserving chat availability across route changes.
• Providing APIs defined by this SIP.
• Persisting the current selected display mode.
The host also provides fixed positioning and layering behavior to ensure
chat visibility remains consistent throughout the application.
We propose 2 display modes for the chat:
Bubble mode
<b>Bubble mode</b>
<img width="1586" height="992" alt="Image"
src="https://github.com/user-attachments/assets/ac6db8a7-fba4-48dd-97f3-5d82d429c209"
/>
<img width="1586" height="914" alt="Image"
src="https://github.com/user-attachments/assets/f3e1a147-e5bb-47bd-b430-d3bff0db6e5e"
/>
<b>Panel Mode</b>
<img width="1597" height="904" alt="Image"
src="https://github.com/user-attachments/assets/d3e82da5-dae4-4a9d-890f-f34883f7fd5a"
/>
<br>
#### Fault Isolation
Chat providers execute within a host-managed boundary.
Failures originating from a chat extension must not affect the rest of the
application.
Examples include:
- Module Federation loading failures.
- Runtime exceptions.
- Provider initialization errors.
If a chat fails to load, the host logs the failure, surfaces an appropriate
notification, and continues operating normally.
The application shell remains functional even when the chat provider is
unavailable.
</details>
<details>
<summary>4.4 Extension Responsibilities</summary>
The registered chat component owns the complete chat experience.
The extension is responsible for:
#### User Interface
- Collapsed bubble UI.
- Expanded panel UI.
- Providing a toggle to switch between display modes by invoking the
setDisplayMode API.
- Branding.
- Icons and badges.
- Layout.
- Responsiveness.
#### Interaction Model
- Open and close behavior.
- Keyboard shortcuts.
- Focus management.
- Accessibility behavior.
- Conversation navigation.
#### Conversation Runtime
- Message history.
- Streaming state.
- Tool execution.
- Persistence.
- Session management.
#### Backend Integration
- LLM communication.
- MCP integration.
- Agent orchestration.
- Tool invocation.
The host does not manage any chat-specific runtime state.
</details>
<details>
<summary>4.5 Registration Example</summary>
Chat extensions register a single provider through the existing view
registration API.
``` typescript
import { chat } from '@apache-superset/core';
import { Panel, Trigger } from 'components/Chat';
chat.registerChat(
chat: { id: 'acme.chat', name: 'Acme Chat' },
trigger: () => Trigger,
panel: () => Panel,
);
```
The registration process remains consistent with existing extension
contribution patterns.
The only difference is that the host applies singleton resolution before
selecting the provider to render.
Runtime UI state such as:
- Notification indicators.
- Unread counts.
- Loading states.
- Thinking indicators.
belongs to the chat component itself rather than the registration descriptor.
This keeps the registry simple while allowing chat implementations complete
control over their user experience.
</details>
### 5. Context and Namespace Model
<details>
<summary>5.1 Overview</summary>
Chat extensions require access to contextual information about the user's
current activity within Superset.
This SIP introduces a namespace-based context model that allows extensions
to consume stable, host-managed APIs rather than depending on internal frontend
implementation details.
The host exposes context through a set of surface-specific namespaces. Each
namespace owns the context for a particular application surface and provides:
• Synchronous state getters.
• Event-based change notifications.
• Stable extension-facing contracts.
• Context aligned with the current user's authorized application view.
Extensions consume these namespaces and compose them into higher-level
context models tailored to their own use cases.
</details>
<details>
<summary>5.2 Design Principles</summary>
The namespace model is guided by the following principles.
#### Stable Extension Contracts
Extensions must depend on documented APIs rather than frontend
implementation details.
In particular, extensions must not depend on:
- Redux slices.
- Store shape.
- Selectors.
- Component-local state.
- Routing implementation details.
This allows Superset to evolve its frontend architecture without breaking
extension integrations.
#### Host-Owned Context Normalization
The host is responsible for transforming application state into semantic
extension-facing contracts.
Extensions consume normalized context rather than deriving it from raw
frontend state.
#### Backend-Authorized Context
Authorization remains a backend responsibility.
Namespaces expose context that has already been scoped by backend APIs
according to the current user's permissions.
Namespaces do not implement authorization logic themselves and should not be
considered security boundaries.
#### Event-Driven Updates
Context changes are propagated through events rather than polling.
Extensions can subscribe to context updates and react immediately when
relevant application state changes.
</details>
<details>
<summary>5.3 Namespaces</summary>
Following the same pattern we did in SQL Lab, we're going to define global
and page-specific namespaces that can be accessed by the chat. In this phase,
we're going to introduce the following namespaces:
<br>
| Namespace | Purpose
|
|-------------|-------------------------------------------------------------|
| `chat` | APIs to register the chat contribution and interact with the
host |
| `navigation`| APIs about routing and navigation context |
<br>
In subsequent phases, we're going to introduce other namespaces like
dashboard, explore, dataset, etc tied to other SIPs that are already in
discussion.
#### Chat Namespace
``` typescript
// packages/superset-core/src/chat/index.ts
import type { Disposable, Event } from "../common";
export interface Chat {
/** The unique identifier for the chat. */
id: string;
/** The display name of the chat. */
name: string;
/** Optional description of the chat, for display in contribution
manifests. */
description?: string;
}
export type DisplayMode = "floating" | "panel";
/**
* Registers a chat provider. The host applies singleton resolution —
* only one chat is active at a time per RFC §4.3.
* Disposing the returned Disposable unregisters the chat.
*
* @param chat The chat descriptor (id, name).
* @param trigger A function returning the collapsed bubble element. Owned by
* the extension — dynamic state such as unread counts and badges lives
here.
* Hidden by the host when in panel mode.
* @param panel A function returning the chat panel element. Mounted by the
* host as a floating overlay in 'floating' mode, or as a layout slot
between
* the header and footer in 'panel' mode. Same component in both modes.
* @returns A Disposable that unregisters the chat when disposed.
*
* @example
* ```typescript
* chat.registerChat(
* { id: 'acme.chat', name: 'Acme Chat' },
* () => <AcmeTrigger />,
* () => <AcmePanel />,
* );
* ```
*/
export declare function registerChat(
chat: Chat,
trigger: () => ReactElement,
panel: () => ReactElement,
): Disposable;
/**
* Returns the active chat descriptor.
*
* @returns The registered Chat descriptor, or undefined if none is
registered.
*/
export declare function getChat(): Chat | undefined;
/**
* Event fired when a chat is registered.
*/
export declare const onDidRegisterChat: Event<Chat>;
/**
* Event fired when a chat is unregistered.
*/
export declare const onDidUnregisterChat: Event<Chat>;
/**
* Opens the chat panel.
*/
export declare function open(): void;
/**
* Closes the chat panel.
*/
export declare function close(): void;
/**
* Returns whether the chat panel is currently open.
*
* @returns True if the chat panel is open.
*/
export declare function isOpen(): boolean;
/**
* Event fired when the chat panel opens.
*/
export declare const onDidOpen: Event<void>;
/**
* Event fired when the chat panel closes.
*/
export declare const onDidClose: Event<void>;
/**
* Returns the current display mode.
*
* @returns The current display mode.
*/
export declare function getDisplayMode(): DisplayMode;
/**
* Sets the display mode.
*
* The host supports both modes on all pages. The host also exposes a mode
* toggle in its chrome — use onDidChangeMode to react to changes initiated
* outside the extension.
*
* @param displayMode The display mode to switch to.
*/
export declare function setDisplayMode(displayMode: DisplayMode): void;
/**
* Event fired when the display mode changes, whether triggered by the
* extension via setDisplayMode() or by the user through host-provided
controls.
*/
export declare const onDidChangeDisplayMode: Event<DisplayMode>;
/**
* Event fired when the user resizes the panel in panel mode.
*
* The host owns the resizer handle and drag interaction. Listen to this
* event to adapt internal layout to the available width.
*/
export declare const onDidResizePanel: Event<{ width: number }>;
// TODO: client actions API — tool availability functions will be added here
// once the client_actions SIP is finalized. The chat namespace is the
// intended integration point between the two SIPs.
```
#### Navigation Namespace
``` typescript
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* @fileoverview Navigation namespace for Superset.
*
* Exposes the current application surface so extensions can react to route
* changes without polling.
*/
import { Event } from '../common';
/**
* The set of top-level application surfaces.
*/
export type Page =
| 'dashboard'
| 'dashboard_list'
| 'explore'
| 'chart_list'
| 'sqllab'
| 'query_history'
| 'saved_queries'
| 'dataset'
| 'dataset_list'
| 'home';
/**
* Returns the current page surface.
*
* @example
* ```typescript
* const page = navigation.getPage();
* if (page === 'dashboard') {
* // do something
* }
* ```
*/
export declare function getPage(): Page;
/**
* Event fired whenever the user navigates to a different surface.
* Use the surface-specific namespace to read entity context after the event.
*
* @example
* ```typescript
* const sub = navigation.onDidChangePage(page => {
* if (page === 'dashboard') {
* // do something
* }
* });
* // later:
* sub.dispose();
* ```
*/
export declare const onDidChangePage: Event<Page>;
```
</details>
<details>
<summary>5.4 Context Composition</summary>
This SIP intentionally does not introduce a host-owned aggregate context
object. Instead, extensions compose the context they require from individual
namespaces.
For example:
``` typescript
const pageContext = {
pageType: navigation.getPageType(),
sqlLab: sqlLab.getCurrentTab(),
// Extensions can still access additional information that is not
available yet in @apache-superset/core using Redux
// We'll continue to increase the available namespaces over time while
other pages become extendable
};
```
The extension assembles a higher-level context tailored to its own
requirements.
The host remains responsible for:
- Context ownership.
- Context normalization.
- Authorization alignment.
The extension remains responsible for:
- Context composition.
- Prompt construction.
- Application-specific interpretation.
This separation avoids introducing a centralized context abstraction while
allowing new surfaces to be added incrementally over time.
</details>
#### 6. Page-Scoped Namespace Guard
<details>
<summary> 6.1 Enforcing API context safety </summary>
To enforce that surface namespaces (**sqlLab, dashboard, explore, dataset**)
are not called from the wrong page, we'll wrap page-specific namespaces
with a **Proxy** at startup via **registerPageNamespace**. Every function
call and event subscription checks the current page before executing. Misuse
throws immediately at the call site rather than returning **undefined**
silently.
``` typescript
function registerPageNamespace<T extends object>(pageType: PageType, impl:
T): T {
return new Proxy(impl, {
get(target, prop) {
const value = target[prop as keyof T];
if (typeof value !== 'function') return value;
return (...args: any[]) => {
if (navigation.getPageType() !== pageType) {
throw new Error(
`'${String(prop)}' is not available outside the '${pageType}'
surface`
);
}
return (value as Function)(...args);
};
},
});
}
```
**Registration at startup:**
``` typescript
// Page-specific namespaces are guarded
export const sqlLab = registerPageNamespace('sqllab', sqlLabImpl);
export const dashboard = registerPageNamespace('dashboard', dashboardImpl);
export const explore = registerPageNamespace('explore', exploreImpl);
export const dataset = registerPageNamespace('dataset', datasetImpl);
// Global namespaces are exported directly — no guard
export const commands = commandsImpl;
export const authentication = authenticationImpl;
export const navigation = navigationImpl;
export const chat = chatImpl;
```
Subscribing to a page-specific event from the wrong page will throw at
subscription time in the same way as other functions
</details>
<details>
<summary> 7. Future Work </summary>
<br>
This SIP establishes the foundational chat extension model and the initial
set of extension-facing APIs.
#### Future work may include:
- Additional page-specific namespaces (dashboard, explore, dataset, SQL Lab).
- Integration with the proposed Client Actions SIP to enable host-mediated
UI actions.
- User preference APIs for chat-specific settings and personalization.
- Richer context contracts and additional application surfaces.
- Support for multiple chat providers and alternative resolution strategies
if future use cases require them.
</details>
### 8. Rejected Alternatives
<details>
<summary>List</summary>
#### Direct Redux Access
Allowing chat extensions to access Redux state directly was considered and
rejected.
This approach would tightly couple extensions to Superset's internal
implementation details, making integrations fragile as frontend architecture
evolves.
#### Host-Owned Aggregate Context Object
A single host-provided context object containing all application state was
considered and rejected.
Instead, this SIP adopts a namespace-based model where extensions compose
the context they need from stable, surface-specific APIs. This avoids
introducing a large centralized contract that would be difficult to evolve over
time.
#### Multi-Provider Rendering
Allowing multiple chat providers to render simultaneously was considered and
rejected.
Chat interactions are inherently conversational and user-focused. Rendering
multiple providers would create competing experiences, increase UI complexity,
and introduce ambiguity regarding which chat should respond.
#### Host-Managed Conversation State
Having the host manage message history, streaming state, and conversation
persistence was considered and rejected.
Conversation state remains fully owned by the chat extension, preserving
separation of responsibilities and maintaining vendor neutrality.
</details>
### 9. Proof of Concept (POC)
<details>
<summary>poc</summary>
A proof of concept has been implemented to validate the proposed chat
extension model and its integration with the existing Superset extension
framework, available at
[test/chat-local](https://github.com/apache/superset/tree/test/chatbot-local)
branch.
#### The prototype demonstrates:
- Registration of a chat provider through the proposed chat.registerChat()
API.
- Singleton provider resolution.
- Rendering of both floating and panel display modes.
- Persistence of display mode across navigation.
- Chat availability across supported application surfaces.
- Integration with the extension loading and registration lifecycle.
- Fault isolation between the host application and the chat implementation.
The proof of concept was used to iterate on the proposed APIs, validate the
singleton rendering model, and confirm that the extension framework can support
a chat integration while preserving separation between host and extension
responsibilities.
The prototype implementation is intended solely for validation purposes and
should not be considered the final production implementation.
</details>
### 10. Related Documents
- [Contribution
types](https://superset.apache.org/developer-docs/extensions/contribution-types2.%20Proposed%20Extension%20Points)
- [Client
actions](https://docs.google.com/document/d/1T0-03Z-3hhXHKJtQtPU5Oq1H1wSJ_SQ7sEOa93I9aFI/edit?tab=t.y1l6igjiow6c)
Add storage API for extensions [[SIP-127] User
Preferences](https://github.com/apache/superset/issues/28047)
### 11. Migration Plan
<details>
<summary>Plan</summary>
Base branch
[enxdev/chat-prototype](https://github.com/apache/superset/tree/enxdev/chat-prototype)
Reference implementation PR: [Introducing a Chat extension in
Superset](https://github.com/apache/superset/pull/41006)
The migration for introducing namespaces to the chat is divided into
multiple phases. The main reason for breaking the work in this way is that when
designing the APIs we need to consider all consumers (host, extensions, chat)
to avoid breaking changes. This means that each API needs discussion and more
time. Breaking the work in this way, allows us to deliver value along the way
and at the same time keep the APIs consistent between consumers.
</details>
### Special thanks
A heartfelt thank you to the following people for their invaluable feedback,
support, and guidance:
- Mehmet Salih Yavuz @msyavuz
- Evan Rusackas @rusackas
- Diego Pucci @geido
- Ville brofeldt @villebro
- Justin Park @justinpark
✍🏼 Enzo, Michael Molina (@michael-s-molina)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]