Chatbot UI

repository·main·Indexed 12 days ago

https://github.com/mckaywrigley/chatbot-ui

An open-source AI chat application for interacting with various LLMs. Version 2.0.0 supports local development via Docker and Supabase, cloud deployment via Vercel, and integration with providers including OpenAI, Azure, Google Gemini, and local Ollama models. Features include Retrieval-Augmented Generation (RAG), streaming responses, and a dedicated /api/command endpoint.

Tokens
8.6K
Snippets
33
Records
36
Agent score
97%

What's inside Chatbot UI

  1. Hosted Quickstart Guide

    main

    To deploy Chatbot UI to the cloud, you need to set up a Supabase backend and a Vercel frontend.

    1. Backend Setup (Supabase)

    1. Create a new project at Supabase.
    2. In Project Settings > General, note the Project Ref and Project ID.
    3. In Settings > API, note the Project URL and Anon key (anon public) and Service role key (service_role).
    4. Enable Email in Authentication > Providers (disabling 'Confirm email' is recommended for personal use).
    5. Configure the migration file supabase/migrations/20240108234540_setup.sql:
      • project_url (line 53): Use your Supabase Project URL.
      • service_role_key (line 54): Use your Supabase Service role key.
    6. Link and push to Supabase:
      supabase login
      supabase link --project-ref <project-id>
      supabase db push

    2. Frontend Setup (Vercel)

    1. Import your GitHub repository to Vercel.
    2. Set Framework Preset to Next.js.
    3. Add the following Environment Variables:
      • NEXT_PUBLIC_SUPABASE_URL
      • NEXT_PUBLIC_SUPABASE_ANON_KEY
      • SUPABASE_SERVICE_ROLE_KEY
      • NEXT_PUBLIC_OLLAMA_URL (if using local Ollama models)
      • OPENAI_API_KEY (optional)
      • AZURE_OPENAI_API_KEY (optional)
      • AZURE_OPENAI_ENDPOINT (optional)
      • AZURE_GPT_45_VISION_NAME (optional)
    4. Click Deploy.
    # Link your hosted project to Supabase
    supabase link --project-ref <project-id>
    
    # Push migrations to the live database
    supabase db push
  2. Update Chatbot UI

    main

    To update your local installation or apply changes to a hosted instance, use the following commands at the root of your repository:

    Update local code:

    npm run update

    Apply migrations to a hosted database: If you are running a hosted instance, you must also run:

    npm run db-push
    npm run update
    npm run db-push
  3. Local Quickstart Guide

    main

    To run Chatbot UI locally, follow these steps to clone the repository, install dependencies, and set up a local Supabase instance for data storage.

    1. Clone and Install

    git clone https://github.com/mckaywrigley/chatbot-ui.git
    cd chatbot-ui
    npm install

    2. Setup Supabase Locally

    You must have Docker installed. Then, install the Supabase CLI based on your OS:

    MacOS/Linux:

    brew install supabase/tap/supabase

    Windows:

    scoop bucket add supabase https://github.com/supabase/scoop-bucket.git
    scoop install supabase

    Start Supabase in the project root:

    supabase start

    3. Configure Environment and SQL

    1. Create your local env file:
      cp .env.local.example .env.local
    2. Run supabase status to get your API URL. Use this value for NEXT_PUBLIC_SUPABASE_URL in .env.local.
    3. Edit supabase/migrations/20240108234540_setup.sql:
      • Replace project_url (line 53) with http://supabase_kong_chatbotui:8000 (default).
      • Replace service_role_key (line 54) with the value from supabase status.

    4. Run the App

    Ensure you are using a compatible Node version (v18+).

    npm run chat
    git clone https://github.com/mckaywrigley/chatbot-ui.git
    cd chatbot-ui
    npm install
    # ... follow remaining steps in guide
  4. Configure document chunking parameters

    main

    The retrieval processing system uses global constants to define how documents are split into smaller pieces for indexing and retrieval. You can reference these constants to understand the granularity of the data being processed:

    • CHUNK_SIZE: The target number of characters per chunk (default: 4000).
    • CHUNK_OVERLAP: The number of characters to overlap between consecutive chunks to maintain context (default: 200).
    export const CHUNK_SIZE = 4000;
    export const CHUNK_OVERLAP = 200;
  5. Configure Prettier formatting and import sorting

    main

    This project uses Prettier for code formatting and the @trivago/prettier-plugin-sort-imports plugin for managing import order.

    Formatting Rules

    • Line Endings: Uses lf (Unix style).
    • Semicolons: Disabled (semi: false).
    • Quotes: Uses double quotes (singleQuote: false).
    • Indentation: Uses spaces instead of tabs (useTabs: false) with a tabWidth of 2.
    • Arrow Function Parens: Avoids parentheses around single arrow function arguments (arrowParens: 'avoid').
    • Trailing Commas: Disabled (trailingComma: 'none').

    Import Sorting Configuration

    Imports are automatically sorted according to a specific hierarchy. The order follows this pattern:

    1. Styles (.scss, .css)
    2. React and Next.js core modules
    3. Third-party modules
    4. Local types and configuration (@/types, @/config)
    5. Library and hooks (@/lib, @/hooks)
    6. UI components and general components (@/components/ui, @/components)
    7. Registry, styles, and app directory (@/registry, @/styles, @/app)
    8. Relative imports (./ or ../)

    Sorting features enabled:

    • importOrderSortSpecifiers: true: Sorts members within a single import statement.
    • importOrderBuiltinModulesToTop: true: Moves built-in Node.js modules to the top.
    • importOrderMergeDuplicateImports: true: Merges multiple imports from the same module into one.
    • importOrderCombineTypeAndValueImports: true: Combines type-only imports with value imports from the same module.
    module.exports = {
      endOfLine: 'lf',
      semi: false,
      useTabs: false,
      singleQuote: false,
      arrowParens: 'avoid',
      tabWidth: 2,
      trailingComma: 'none',
      importOrder: [
        '^.+\.scss$',
        '^.+\.css$',
        '^(react/(.*)$)|^(react$)',
        '^(next/(.*)$)|^(next$)',
        '<THIRD_PARTY_MODULES>',
        '',
        '^types$',
        '^@/types/(.*)$',
        '^@/config/(.*)$',
        '^@/lib/(.*)$',
        '^@/hooks/(.*)$',
        '^@/components/ui/(.*)$',
        '^@/components/(.*)$',
        '^@/registry/(.*)$',
        '^@/styles/(.*)$',
        '^@/app/(.*)$',
        '',
        '^[./]'
      ],
      importOrderSeparation: false,
      importOrderSortSpecifiers: true,
      importOrderBuiltinModulesToTop: true,
      importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
      importOrderMergeDuplicateImports: true,
      importOrderCombineTypeAndValueImports: true
    }
  6. Environment Variables Reference

    main

    Chatbot UI uses environment variables to configure the Supabase connection and API keys. If these variables are set, the corresponding inputs in the user settings UI will be disabled.

    Supabase Configuration:

    • NEXT_PUBLIC_SUPABASE_URL: The Supabase API URL.
    • NEXT_PUBLIC_SUPABASE_ANON_KEY: The Supabase anon public key.
    • SUPABASE_SERVICE_ROLE_KEY: The Supabase service_role key (sensitive).

    Model Provider Configuration:

    • NEXT_PUBLIC_OLLAMA_URL: The URL for local Ollama models (default: http://localhost:11434).
    • OPENAI_API_KEY: OpenAI API key.
    • AZURE_OPENAI_API_KEY: Azure OpenAI API key.
    • AZURE_OPENAI_ENDPOINT: Azure OpenAI endpoint.
    • AZURE_GPT_45_VISION_NAME: Azure GPT-4 vision model name.
  7. Extract media type and base64 data from Data URLs

    main

    These utilities allow you to parse Data URLs (commonly used for images or file attachments in chat interfaces):

    • getMediaTypeFromDataURL(dataURL): Extracts the MIME type (e.g., image/png) from the prefix of a base64 Data URL. Returns null if the format is invalid.
    • getBase64FromDataURL(dataURL): Extracts the raw base64 encoded string from the Data URL, stripping the prefix. Returns null if the format is invalid.
    import { getMediaTypeFromDataURL, getBase64FromDataURL } from '@/lib/utils';
    
    const dataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==";
    
    const type = getMediaTypeFromDataURL(dataURL); // "image/png"
    const base64 = getBase64FromDataURL(dataURL); // "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=="
  8. Validate an API key with checkApiKey()

    main

    Use checkApiKey(apiKey, keyName) to ensure a required API key is present before proceeding with a request. If the apiKey is null or an empty string "", the function throws an error containing the provided keyName.

    This is useful for early validation in server actions or API routes to provide clear error messages to the user or logs.

    import { checkApiKey } from '@/lib/server/server-chat-helpers'
    
    function handleChatRequest(userApiKey: string | null) {
      try {
        checkApiKey(userApiKey, "OpenAI");
        // If it passes, proceed with the request
      } catch (e) {
        // Handle error: "OpenAI API Key not found"
      }
    }
  9. Process streaming chat responses

    main

    The processResponse function consumes a readable stream from the chat response and updates the chat messages in real-time.

    Streaming Logic:

    • Hosted Providers: Treats chunks as direct text.
    • Local (Ollama): Parses chunks as newline-separated JSON objects, extracting message.content from each.

    It updates the lastChatMessage in the setChatMessages state as new text arrives.

    const fullText = await processResponse(
      response,
      lastChatMessage,
      isHosted,
      controller,
      setFirstTokenReceived,
      setChatMessages,
      setToolInUse
    );
  10. Use the browser-side Supabase client

    main

    The project exports a pre-configured Supabase client instance named supabase for use in browser-side operations. This client is initialized using @supabase/ssr and is typed with the project's Database schema. It automatically uses the NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY environment variables for authentication and connection.

    import { supabase } from '@/lib/supabase/browser-client'
    
    // Example usage: querying data from the browser
    const { data, error } = await supabase.from('profiles').select('*')
    if (error) console.error(error)
  11. Handle local chat via Ollama

    main

    The handleLocalChat function manages chat requests to a local LLM provider (typically Ollama). It builds the final message payload and fetches the response from the configured NEXT_PUBLIC_OLLAMA_URL.

    It handles the streaming response and updates the chat state as tokens arrive.

    await handleLocalChat(
      payload,
      profile,
      chatSettings,
      tempAssistantMessage,
      isRegeneration,
      newAbortController,
      setIsGenerating,
      setFirstTokenReceived,
      setChatMessages,
      setToolInUse
    );
  12. Retrieve the current user profile on the server with getServerProfile()

    main

    Use getServerProfile() in server-side components or API routes to fetch the authenticated user's profile from the Supabase profiles table.

    This function automatically:

    1. Initializes a Supabase client using NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY.
    2. Retrieves the current user via Supabase Auth.
    3. Fetches the corresponding row from the profiles table where user_id matches the authenticated user.
    4. Injects relevant API keys from the server's environment variables into the returned profile object.

    Note: This function will throw an error if the user is not authenticated or if no profile exists in the database.

    import { getServerProfile } from '@/lib/server/server-chat-helpers'
    
    export default async function ServerComponent() {
      try {
        const profile = await getServerProfile();
        // profile now contains database fields + injected environment API keys
        console.log(profile);
      } catch (error) {
        console.error("Failed to load profile:", error.message);
      }
    }