Overview

This guide provides step-by-step instructions to implement chat persistence using Firebase Firestore within a Next.js application powered by the Thesys C1 GenUI SDK. By following this guide, you’ll learn how to store, retrieve, and manage chat threads and messages, enabling a seamless user experience across sessions.

The complete code for the example referenced in this guide can be found in the Thesys examples repository.

Implementation Steps

1

Set up Firebase Project & SDK

First, ensure your Firebase project is ready and the Firebase SDK is configured in your Next.js application.

  1. Go to the Firebase Console,and copy the firebaseConfig object.

Here are the detailed step-by-step instructions on how to setup the database and fetch the firebase configuration:

  1. Update Firebase Config File (src/firebaseConfig.ts): Replace the placeholder values in firebaseConfig with your actual credentials in src/firebaseConfig.ts:

    src/firebaseConfig.ts
    import { initializeApp, getApp, getApps } from "firebase/app";
    import { getFirestore } from "firebase/firestore";
    
    // IMPORTANT: Replace these with your actual Firebase project configuration!
    const firebaseConfig = {
      apiKey: "YOUR_API_KEY",
      authDomain: "YOUR_AUTH_DOMAIN",
      projectId: "YOUR_PROJECT_ID",
      storageBucket: "YOUR_STORAGE_BUCKET",
      messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
      appId: "YOUR_APP_ID",
      measurementId: "YOUR_MEASUREMENT_ID"
    };
    
    // Initialize Firebase
    let app;
    if (!getApps().length) {
      app = initializeApp(firebaseConfig);
    } else {
      app = getApp();
    }
    
    const db = getFirestore(app);
    
    export { db };
    
2

Create Thread Service for Firebase

Implement a service layer that handles persistence logic using Firestore.

Create src/services/threadService.ts:

1. Core CRUD Functions: Implement functions to create, read, update, and delete threads and messages.

Refer to the complete example threadService.ts for full implementations of others like getThreadList, getUIThreadMessages, getLLMThreadMessages, updateMessage, deleteThread, and updateThread similarly.

3

Integrate Service with Frontend

Configure C1Chat component (src/app/page.tsx) to use the functions in threadService.ts for data operations.

  1. Configure C1 SDK Hooks: Pass the threadService functions to the useThreadListManager and useThreadManager hooks.

    src/app/page.tsx (Hook Configuration)
      const threadListManager = useThreadListManager({
        fetchThreadList: () => getThreadList(),
        deleteThread: (threadId) => deleteThread(threadId),
        updateThread: (t) => updateThread({ threadId: t.threadId, name: t.title }),
        onSwitchToNew: () => {
          replace(`${pathname}`);
        },
        onSelectThread: (threadId) => {
          const newSearch = `?threadId=${threadId}`;
          replace(`${pathname}${newSearch}`);
        },
        createThread: (message) => {
          return createThread(message.message!);
        },
      });
    
      const threadManager = useThreadManager({
        threadListManager,
        loadThread: async (threadId) => await getUIThreadMessages(threadId),
        onUpdateMessage: async ({ message }) => {
          await updateMessage(threadListManager.selectedThreadId!, message);
        },
        apiUrl: "/api/chat",
      });
    

    See the complete example page.tsx for detailed hook setup with imports.

4

Add Backend Chat API Route

Add Chat Api route to use Thesys C1 Api along with functions in threadService.ts for fetching historical messages and saving new ones.

  1. Fetch History for LLM: Before calling the LLM, fetch the existing messages for the thread using getLLMThreadMessages.

    src/app/api/chat/route.ts (Fetch History)
    const llmMessages = await getLLMThreadMessages(threadId);
    
    const runToolsResponse = client.beta.chat.completions.runTools({
      model: "c1-nightly",
      messages: [
        ...llmMessages, // Add previous messages
        {
          role: "user",
          content: prompt.content!,
        },
      ],
      stream: true,
      tools,
    });
    
  2. Save Messages on Stream End: In the on("end") event of the LLM stream, use addMessages to store the new user prompt and all assistant/tool messages generated in that turn.

    src/app/api/chat/route.ts (Save Messages)
    runToolsResponse.on("end", async () => {
      if (isError) {
        return;
      }
    
      const runToolsMessagesWithId = allRunToolsMessages.map((m, index) => {
        const id =
          allRunToolsMessages.length - 1 === index // for last message (the response shown to user), use the responseId as provided by the UI
            ? responseId
            : crypto.randomUUID();
    
        return {
          ...m,
          id,
        };
      });
    
      const messagesToStore = [prompt, ...runToolsMessagesWithId];
    
      await addMessages(threadId, ...messagesToStore);
    });
    

    Refer to the complete example /api/chat/route.ts for context.

Running and Testing

  1. Ensure your Firebase credentials are correctly set in src/firebaseConfig.ts.
  2. Ensure your THESYS_API_KEY is set in .env.
  3. Run npm run dev.
  4. Test creating new chats, sending messages, switching between chats, and refreshing the page to see if history persists. Check the Firebase console to see data being written to Firestore.

Test it Out!

Conclusion

By following this guide, you’ve integrated Firebase Firestore for robust chat persistence in your Thesys C1 GenUI application. This setup provides a scalable backend for your chat data, enhancing user experience by preserving conversation history.