BotConfiguration constructor

const BotConfiguration({
  1. required String userID,
  2. required String projectSecretKey,
  3. String? userIdSignature,
  4. LocalizedText? welcomeMessage,
  5. LocalizedText? name,
  6. LocalizedText? systemInstructions,
  7. @Default.new('en') String defaultLocale,
  8. @Default.new(['en']) List<String> supportedLocales,
  9. String? currentLocale,
  10. String? allowedOrigins,
  11. @Default.new('Roboto') String? fontFamily,
  12. String? color,
  13. @unfreezed Uint8List? avatar,
  14. @Default.new(false) bool isPreviewMode,
  15. @Default.new(false) bool isReadOnly,
  16. @Default.new(true) bool showAvatar,
  17. @Default.new(false) bool hideBarsOnScroll,
  18. @Default.new(true) bool enableUserExport,
  19. @Default.new(true) bool enableClearHistory,
  20. @Default.new(true) bool enableFileAttach,
  21. @Default.new(false) bool dynamicLanguageEnabled,
  22. @Default.new(true) bool aiDisclosureEnabled,
  23. LocalizedText? aiDisclosureText,
  24. @Default.new(false) bool aiDisclosureRequireAck,
  25. @Default.new(true) bool messageReportingEnabled,
  26. String? aiInfoUrl,
  27. String? privacyUrl,
  28. @Default.new(false) bool aiDataReuseNotice,
  29. @Default.new(FabConfiguration()) FabConfiguration fabConfiguration,
  30. @Default.new(SoundConfiguration()) SoundConfiguration soundConfiguration,
  31. ChatAppearance? chatAppearance,
  32. ChatAppearance? chatAppearanceDark,
  33. @Default.new(true) bool showThemeToggle,
  34. @Default.new('system') String defaultThemeMode,
  35. Map<String, dynamic>? userContext,
  36. String? authToken,
})

Implementation

const factory BotConfiguration({
  /// Unique identifier for the user interacting with the bot.
  required String userID,

  /// Public project identifier shipped to the widget. Used by the
  /// chat backend to look up the project; safe to inline in HTML or
  /// app bundles (it's the equivalent of a Stripe publishable key).
  /// By itself it cannot read another user's history — that requires
  /// a matching [userIdSignature].
  required String projectSecretKey,

  /// HMAC-SHA256 hex digest of [userID] keyed by the project's
  /// `identity_secret`, computed on the host app's backend and
  /// forwarded here.
  ///
  /// The chat backend verifies this server-side before serving
  /// `/api/chat/*` for the given [userID]. Without it, anyone who
  /// reads the install snippet HTML can pair the public
  /// [projectSecretKey] with an arbitrary [userID] and read that
  /// user's history.
  ///
  /// Optional for backwards-compat with projects that haven't yet
  /// enabled `identity_verification_required` in the dashboard's
  /// Data & Privacy section; required once that toggle is on.
  ///
  /// NEVER bake the `identity_secret` itself into the SDK or the
  /// host app's binary — compute the signature on your backend and
  /// ship only the resulting digest. See the dashboard's Install
  /// tab for backend-side snippets in Node, Python, and Ruby.
  String? userIdSignature,

  /// Initial message displayed when the chat opens (localized).
  LocalizedText? welcomeMessage,

  /// Display name for the chatbot (localized).
  LocalizedText? name,

  /// System instructions that guide the bot's behavior (localized).
  LocalizedText? systemInstructions,

  /// Default locale code for the chat interface (e.g., 'en', 'fr', 'es').
  @Default('en') String defaultLocale,

  /// List of supported locale codes
  @Default(['en']) List<String> supportedLocales,

  // The locale code for the chat interface (e.g., 'en', 'fr', 'es').
  String? currentLocale,

  /// Comma-separated list of allowed origins for CORS.
  String? allowedOrigins,

  /// Font family for chat text. Defaults to 'Roboto'.
  @Default('Roboto') String? fontFamily,

  /// Primary color for the chat interface in hex format. When null, the
  /// SDK inherits the host app's theme colour (`ColorScheme.primary`) —
  /// see `_getPrimaryColor` in chat_screen. Left intentionally without a
  /// default so "unset" means "inherit from app" rather than a forced hex.
  String? color,

  /// Avatar image data. Note: This is excluded from JSON serialization.
  @unfreezed Uint8List? avatar,

  /// Whether the bot is running in preview mode.
  @Default(false) bool isPreviewMode,

  /// Whether the chat is in read-only mode (no user input allowed).
  /// When true, the chat input field will be disabled.
  @Default(false) bool isReadOnly,

  /// Whether to show the bot avatar next to bot MESSAGES (and the
  /// typing indicator). The header avatar is unconditionally shown —
  /// this flag only gates the per-bubble avatar circle.
  ///
  /// When `true` (default) each non-user message is preceded by the
  /// configured [avatar] circle. When `false`, the avatar is hidden
  /// and the bubble grows a small WhatsApp-style tail at its
  /// top-left to keep the sender visually anchored.
  ///
  /// Host apps that want a denser, avatar-less message list (e.g.
  /// compact in-app coaches) can flip this without touching the
  /// bubble code.
  @Default(true) bool showAvatar,

  /// When `true`, the chat header and the message-input bar auto-hide as
  /// the user scrolls up through older messages, and slide back in when
  /// they scroll back toward the newest message — maximising reading
  /// space on a full-page chat. Defaults `false` (both bars stay pinned).
  ///
  /// Surfaced via the server's `/api/project/config/` payload
  /// (`hide_bars_on_scroll`) so tenants toggle it from the dashboard; the
  /// SDK OR-merges it with this host value so a host can opt in locally
  /// too. See [ChatViewModel.initialize].
  @Default(false) bool hideBarsOnScroll,

  /// Whether to surface an "Export chat" item in the chat header's
  /// more-options menu. When `true` (default) the user can download
  /// their own transcript as a `.zip` and hand it to the platform
  /// share sheet. When `false`, the menu item is hidden — useful for
  /// hosts whose privacy posture or app-store category requires
  /// suppressing data-export affordances.
  ///
  /// This is also surfaced via the server's `/api/project/config/`
  /// payload (`enable_user_export`) so a host can toggle it from
  /// the dashboard without a widget redeploy. The host-supplied
  /// value here wins per the standard host-overrides-server
  /// precedence; see [ChatViewModel.initialize].
  @Default(true) bool enableUserExport,

  /// Whether to surface a "Clear chat history" item in the chat
  /// header's more-options menu. When `true` (default) the user can
  /// wipe their own conversation; when `false`, the item is hidden.
  ///
  /// Surfaced via the server's `/api/project/config/` payload
  /// (`enable_clear_history`) — same host-overrides-server precedence
  /// as [enableUserExport].
  @Default(true) bool enableClearHistory,

  /// Whether to render the paperclip attach button in the chat
  /// input. When `true` (default) the user can pick an image to
  /// send with their message; when `false`, the button is hidden.
  ///
  /// Surfaced via the server's `/api/project/config/` payload
  /// (`enable_file_attach`) — same host-overrides-server precedence
  /// as [enableUserExport].
  @Default(true) bool enableFileAttach,

  /// When `true`, the bot may switch the chat language live (via the
  /// backend `change_language` tool) and the SDK exposes a manual
  /// language picker in the header. Surfaced via the server config
  /// (`dynamic_language_enabled`). Picker also requires 2+
  /// [supportedLocales].
  @Default(false) bool dynamicLanguageEnabled,

  /// When `true` (default), the chat renders a light "You're chatting
  /// with an AI" disclosure inside the chat surface. Surfaced via the
  /// server config (`ai_disclosure_enabled`).
  @Default(true) bool aiDisclosureEnabled,

  /// Per-locale disclosure text. Null = use the SDK's localized
  /// default (`ai_disclosure` ARB key). Surfaced via the server config
  /// (`ai_disclosure_text`).
  LocalizedText? aiDisclosureText,

  /// When `true`, the disclosure shows a one-time "OK" the user must
  /// acknowledge (persisted locally). Surfaced via the server config
  /// (`ai_disclosure_require_ack`).
  @Default(false) bool aiDisclosureRequireAck,

  /// When `true`, bot messages expose a long-press "Good/Bad response"
  /// menu; "Bad response" opens a reason sheet that reports the message
  /// (App Store UGC / Guideline 1.2). On by default. Surfaced via the
  /// server config (`message_reporting_enabled`).
  @Default(true) bool messageReportingEnabled,

  /// "Learn more" target for the disclosure chip — an About-AI page.
  /// Falls back to [privacyUrl] when empty. Surfaced via the server
  /// config (`ai_info_url`).
  String? aiInfoUrl,

  /// Privacy page link; fallback "Learn more" target. Surfaced via the
  /// server config (`privacy_url`).
  String? privacyUrl,

  /// When `true`, the chip shows an extra line that chats may be used
  /// to improve the service (display-only). Surfaced via the server
  /// config (`ai_data_reuse_notice`).
  @Default(false) bool aiDataReuseNotice,

  /// Configuration for the floating action button appearance.
  @Default(FabConfiguration()) FabConfiguration fabConfiguration,

  /// Configuration for sound effects.
  @Default(SoundConfiguration()) SoundConfiguration soundConfiguration,

  /// Tenant-configurable chat-surface colours. When the server
  /// returns a `chat_appearance` block in `/api/project/config/`,
  /// the repository inflates it into this field; hosts can also
  /// pass one explicitly to override at the per-session level.
  ///
  /// Render-time precedence (lowest → highest):
  ///   1. SDK's MD3 defaults derived from the ambient [ColorScheme].
  ///   2. This field (server-set or host-set).
  ///   3. `Theme.of(context).extension<ChatColors>()` on the host's
  ///      MaterialApp — wins when present, lets a host force a
  ///      brand-specific palette regardless of what the dashboard
  ///      says.
  ChatAppearance? chatAppearance,

  /// Dark-mode counterpart to [chatAppearance]. Selected when the active
  /// theme brightness is dark. Same precedence rules as [chatAppearance].
  ChatAppearance? chatAppearanceDark,

  /// Whether the light/dark theme switcher appears in the header's
  /// more-options menu. Server-gated via `show_theme_toggle`.
  @Default(true) bool showThemeToggle,

  /// Initial theme mode before the user overrides it — 'light', 'dark',
  /// or 'system' (follows the host device brightness).
  @Default('system') String defaultThemeMode,

  /// Free-form host-app context forwarded to the bot on every
  /// `/api/chat/` request and surfaced into the system prompt.
  ///
  /// Use this to give the LLM facts about the current user that aren't
  /// already visible from the message history — e.g. Pro entitlement
  /// state, current hand-analysis ID, last design they viewed, skin
  /// tone, accessibility preferences. The backend reads it from the
  /// request body and inlines it into the system prompt under a
  /// dedicated `<user_context>` section.
  ///
  /// Keep payloads small (a few KB max) — every key here is tokenised
  /// per turn. Don't include large blobs, image bytes, or PII the bot
  /// doesn't need. Map values must be JSON-serialisable.
  ///
  /// Example:
  /// ```dart
  /// BotConfiguration(
  ///   userID: '42',
  ///   projectSecretKey: '...',
  ///   userContext: {
  ///     'pro': true,
  ///     'hand_analysis_id': 'h_abc',
  ///     'last_design_id': 789,
  ///     'skin_tone_fitzpatrick': 4,
  ///   },
  /// )
  /// ```
  Map<String, dynamic>? userContext,

  /// End-user JWT (or equivalent bearer token) forwarded to the bot
  /// backend on every chat request so authenticated downstream tools
  /// can attribute calls to the right account.
  ///
  /// Use this when the host app has its own identity layer and the bot
  /// needs to call a backend API that requires that user's auth — e.g.
  /// AI Nail's `/api/nail/*` endpoints, where ``design_save`` must
  /// attach to the calling user's library, ``tryon_start`` to their
  /// hand photo, etc.
  ///
  /// The token is sent only in-flight, never persisted by widget_chat.
  /// Read-side rotation is on the host: a new token here on the next
  /// `updateConfiguration` reaches every subsequent request. Pass
  /// ``null`` (the default) when the bot has no authenticated tools.
  ///
  /// Backend-side handling is described in
  /// `flutterbot_api/chat_api/tools/api_executor.py` — the token lands
  /// as `Authorization: Bearer <token>`, never overrides a tool's own
  /// service-account header, and is redacted from ToolExecution rows.
  String? authToken,
}) = _BotConfiguration;