10 KiB
10 KiB
Conduit (Flutter) vs Open-WebUI Web Client: Architecture Comparison, Issues, and Improvements
Executive Summary
- Conduit aligns closely with Open-WebUI’s backend contract (auth, chat, tasks, sockets, files, i18n).
- Largest gaps: inconsistent background task flow vs SSE, partial parity for socket event mirroring, file/image handling differences, settings sync, and error UX.
- This document lists concrete issues with targeted improvements that match AGENTS.md guardrails (small, surgical edits, no new deps unless justified).
Scope and Criteria
- Compared major areas: networking, auth/token, chat send/streaming, sockets, files, settings, i18n, and error handling.
- Cross-referenced key files in
lib/andopenwebui-src/to identify parity gaps and opportunities.
Networking & API Layer
- Flutter:
lib/core/services/api_service.dartusesDiowithApiAuthInterceptorandApiErrorInterceptor, plus debug wrappers.- Base URL:
ServerConfig.url. - Validates statuses
<400. Adds custom headers safely.
- Base URL:
- Web:
openwebui-src/src/lib/apis/**usesfetch, explicitAuthorizationheader, and returns JSON/SSE readers.
Issues
- ApiErrorInterceptor file is referenced but not present in the workspace snapshot (potentially moved/renamed). Risk of inconsistent error shaping.
lib/core/services/connectivity_service.dartdefines a simpledioProviderthat is not wired to project’s configuredDio. Possible confusion or unused provider.
Improvements
- Ensure a single
Diosource of truth. IfdioProvideris unused, remove it to avoid drift. - Verify the actual error interceptor path exists and standardizes error payloads (ensure 401, validation errors, and transport errors surface with actionable messages). If missing, add an error transformer consistent with web client’s
handleOpenAIErrorpatterns.
Authentication & Tokens
- Flutter:
AuthStateManagerpersists token viaOptimizedStorageServiceandFlutterSecureStorage. AddsAuthorization: BearerinApiAuthInterceptoronly for required or optional endpoints. - Web: On login, sets
localStorage.tokenand uses token infetchheaders;socketuser-joinis also emitted.
Issues
- Flutter’s auth interceptor strictly blocks required endpoints without token (401 synthesis). Web sometimes tries endpoint and shows backend error. Flutter approach is OK, but ensure optional endpoints are fully listed to match backend behavior.
Improvements
- Keep the strict check but review endpoint lists in
ApiAuthInterceptorto match Open-WebUI’s optional/required matrix (e.g., config and public assets). - Add a token-expiry check cadence similar to web layout’s interval if not already running, or rely on backend 401 → logout flow.
Chat Send, Streaming, Tasks, and Sockets
- Web: Primarily streams via SSE (
chatCompletion) and relays to socket channels; also supports background task flow withtask_idand event mirroring (chat-events,channel-events). - Flutter:
ApiService.sendMessagesupports SSE-like stream but currently forces background tools flow (useBackgroundTasks = true), attachessession_id/id, and polls chat until tool sections are done. Socket integration exists viaSocketServiceandstreaming_helper.dartto listen to lines or JSON payloads.
Issues
- SSE vs Task Mode: Flutter hard-forces background flow, which disables direct SSE streaming and can add latency. Web uses SSE for pure completions and task mode when tools/search/image-gen are active.
- Dynamic Channel Handling:
streaming_helper.darthas line handlers andchatHandler, but suppression flags and switching between SSE and socket content may not mirror all event types from web (chat:message:delta,chat:message:files,chat:message:error,source/citation, confirmation, etc.). - Stop/Cancel: Web manages
taskIdson active chat to stop. Flutter exposesstopTaskbut does not surface a complete UI flow in providers/widgets here.
Improvements
- Restore dual-path streaming:
- Use pure SSE (no
session_id/id) when no tools/web-search/image-gen are used to reduce latency and match web UX. - Use background task + dynamic channel session only when features require it.
- Use pure SSE (no
- Expand event handling parity in
streaming_helper.dartto map Open-WebUI event types to mobile UI updates (files, citations, errors, followups). - Wire cancel/stop control in UI: keep the task API and expose current
taskIdsper chat to stop ongoing responses.
Files and Attachments
- Web: Uses
/api/v1/files/with streaming status for processing; images can be inlined in content; shows progress. - Flutter:
FileAttachmentServiceconverts images to data-URL instead of uploading; non-images upload viaApiService.uploadFile(multipart).AttachmentUploadQueueexists for background/offline retries.
Issues
- Image Handling Divergence: Converting images to data URLs increases payload size and memory; web often uploads and references by id/URL, enabling server-side processing (RAG, OCR, etc.).
- Processing Status: Web listens to file processing SSE to surface parse status; Flutter does not reflect live processing feedback after upload.
Improvements
- Prefer consistent behavior: upload images too (like web) and store file
id, letting server handle compression/processing. - Optionally implement file processing progress by consuming
/files/{id}/statusSSE (if available) and reflect it inFileAttachmentWidget.
Settings and Preferences
- Web:
settingsstore persists UI prefs, updates to backend viaupdateUserSettingsunder/users/user/settings/updatewith{ ui: settings }. - Flutter:
SettingsServicepersists locally viaSharedPreferences;userSettingsProviderfetches server settings viaapi.getUserSettings()returningUserSettingsmodel.
Issues
- Potential divergence between local-only settings and backend user settings (
uinamespace). Some settings (e.g., haptics, stream_response default, chat direction) exist on web but not mirrored in Flutter’sUserSettings.
Improvements
- Align
UserSettingsfields with backenduistructure where applicable; when user is authenticated, prefer server-backed settings for cross-device consistency and fall back to local when offline. - When settings change, POST updates to backend similar to web (
/users/user/settings/update). Add a small merge strategy: local-only keys remain local, cross-device keys go to server.
Internationalization (i18n)
- Web:
i18nextwith lazy-loaded JSON; language auto-detect and backend default locale; setslangattribute. - Flutter:
gen-l10nARB files withAppLocalizations, docs indocs/localization.mdand CI step.
Issues
- Parity of strings: ensure Flutter ARB keys cover web parity for shared concepts (errors, chat UI hints, settings labels). Some strings appear hardcoded in Flutter widgets (e.g., "Attachments").
Improvements
- Move visible strings to ARB and reuse placeholders/ICU. Mirror important UI preferences and messages so translation workflows match.
Error Handling and UX
- Web: Centralized toast errors (e.g.,
handleOpenAIErrorpath, Error.svelte rendering message/detail with fallbacks). - Flutter:
ApiErrorInterceptor(referenced),ErrorBoundary, and some ad-hocdebugPrints.
Issues
- Missing or moved
ApiErrorInterceptorrisks inconsistent UX. Error messages from task flow/polling are not always promoted to UI components.
Improvements
- Ensure API errors map to user-friendly messages, with context (network vs auth vs provider error). Surface task/polling errors in the chat UI just like streaming failures.
Connectivity and Offline
- Flutter:
connectivity_service.dartprovides online/offline providers;AttachmentUploadQueueretries. - Web: Browser online/offline events; no background file queue.
Improvements
- Good mobile-first enhancement: keep AttachmentUploadQueue. Consider surfacing an inline banner or per-file retry affordances.
Sockets
- Web: Socket.IO setup in
+layout.svelte, emitsuser-join, registerschat-eventsandchannel-events, forwards streamed lines to channel. - Flutter:
SocketServicemirrors handshake headers, reconnect flow, and event subscription methods.
Issues
- Ensure socket path
/ws/socket.ioand headers match server expectations across reverse proxies; feature flags in mobile to prefer WS-only if environment requires it.
Improvements
- Expose transport mode in settings (already present,
socket_transport_mode), and surface diagnostics screen to test connectivity.
Security & Privacy
- Flutter: Secure storage for token, avoids logging secrets, custom headers filtered. Web: localStorage for token.
Improvements
- Maintain secure storage for mobile; avoid verbose logging of payloads. Redact tokens in debug logs.
Concrete Action Items (Minimal, Targeted)
- Streaming mode parity
- In
ApiService.sendMessage, choose SSE when no tools/web_search/image_generation; use background task session only when required. - Keep
streaming_helper.dartsuppression flags but extend to support more event types from web.
- Files
- Upload images via
/api/v1/files/instead of converting to data URLs. Storeidand let server serve compressed/derived forms. - Optionally consume processing status SSE for richer feedback.
- Settings sync
- Map
UserSettingsto backenduifields; on change, POST to/users/user/settings/updatesimilar to web.
- Error handling
- Ensure
ApiErrorInterceptorexists and standardizes Dio exceptions. Add chat-level error surfacing in providers/widgets for background task flow.
- Cleanup and consistency
- Remove or wire the unused
dioProviderfromconnectivity_service.dart. - Move literal strings like "Attachments" into ARB files.
- Diagnostics
- Add a hidden debug screen to exercise socket connectivity and API endpoints (a wrapper exists in
api_service.debugApiEndpoints).
Deferred / Optional (Non-breaking)
- Stop/Cancel UX parity: expose active
taskIdsand provide a Stop button for ongoing responses. - Model/tool server parity: ensure
tool_serverspayload and function-calling hints are preserved (already partially implemented). - Performance: Consider
StreamTransformerbackpressure tuning instreaming_helper.dartfor low-end devices.
Justification and Guardrails
- Minimal changes; no new dependencies required.
- Aligns with Open-WebUI semantics for better feature parity and user expectations.
- Improves robustness and UX while preserving current architecture and patterns (Riverpod, Dio, interceptors, providers).