feat: add Open WebUI AI chat integration and widget

Adds a new "openWebUi" integration kind (under a new "ai" category) and an
AI Chat widget for chatting with models served by Open WebUI.

- Integration: Bearer API key auth, model list, native chat history CRUD, and
  token-by-token streaming chat completions (SSE).
- Widget: in-input model picker, streaming responses, and an in-widget history
  panel. Reconstructs conversations from Open WebUI's message tree with a flat
  fallback.
- Streaming is delivered over the WebSocket subscription link.
- Adds integration and widget documentation.
This commit is contained in:
Lok3sh-Kumar committed 2026-06-27 14:12:48 +05:30
1 parent f3608b26a0
commit 005c2e4cd1
25 files changed
+1290

No files matched your search

@@ -195,6 +195,7 @@ export type HomarrDocumentationPath =
| "/docs/integrations/ntfy"
| "/docs/integrations/nzbget"
| "/docs/integrations/open-media-vault"
| "/docs/integrations/open-webui"
| "/docs/integrations/opnsense"
| "/docs/integrations/overseerr"
| "/docs/integrations/paperless-ngx"
@@ -59,5 +59,6 @@ export const integrationDocSlugs: Record<IntegrationKind, string | null> = {
peaNut: "peanut",
beszel: "beszel",
archiveTeamWarrior: null,
openWebUi: "open-webui",
mock: null,
};
@@ -51,4 +51,5 @@ export const widgetDocSlugs: Record<WidgetKind, string | null> = {
beszelAlerts: "beszel-alerts",
beszelSystemStats: "beszel-system-stats",
customApi: "custom-api",
openWebUi: "open-webui",
};
+10
View File
@@ -504,6 +504,15 @@ export const integrationDefs = {
defaultUrl: "http://localhost:8001",
documentationUrl: null,
},
openWebUi: {
name: "Open WebUI",
secretKinds: [["apiKey"]],
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/open-webui.svg",
category: ["ai"],
documentationUrl: createDocumentationLink("/docs/integrations/open-webui"),
defaultPort: 8080,
apiKeySettingsPath: "/?settings=open#account",
},
// This integration only returns mock data, it is used during development (but can also be used in production by directly going to the create page)
mock: {
name: "Mock",
@@ -617,6 +626,7 @@ export const integrationCategories = [
"documents",
"mediaLibrary",
"uptimeMonitoring",
"ai",
] as const;
export type IntegrationCategory = (typeof integrationCategories)[number];
@@ -40,6 +40,7 @@ export const widgetIntegrationSupport: Partial<Record<WidgetKind, readonly Integ
audioStats: ["navidrome", "audiobookshelf"],
vpn: getIntegrationKindsByCategory("vpn"),
anchorNote: ["anchor"],
openWebUi: ["openWebUi"],
};
export const getWidgetKindsForIntegration = (integrationKind: IntegrationKind): WidgetKind[] => {
+1
View File
@@ -49,6 +49,7 @@ export const widgetKinds = [
"beszelAlerts",
"beszelSystemStats",
"customApi",
"openWebUi",
] as const;
export type WidgetKind = (typeof widgetKinds)[number];