Build and Deploy a SaaS AI Agent Platform | Next.js 15, React, Better Auth, Polar | Part 2/2

By Code With Antonio

TechnologyAIBusiness
Share:

Key Concepts

  • AI Agents: AI personas with custom instructions that can join video calls.
  • Searchable Transcripts: Textual representations of meeting conversations that can be searched for specific keywords.
  • AI Chat: An AI-powered chat functionality that understands the full meeting context and can answer questions.
  • Subscriptions and Free Trials: Implementation of payment plans and trial periods using Polar.sh.
  • Deployment: The process of making the application live and accessible to users.
  • Stream Chat: A service used for implementing real-time chat functionality.
  • Polar.sh: A platform used for managing subscriptions and payments.
  • Drizzle ORM: An Object-Relational Mapper used for interacting with the database.
  • Injest: A service used for background job processing.
  • Vercel: A platform used for deploying web applications.

Transcript and Chat Implementation

Adding Transcript Functionality

  1. getTranscript Procedure:
    • A protected procedure is created to fetch the meeting transcript.
    • It accepts the meeting ID as input.
    • It retrieves the meeting details from the database, ensuring the logged-in user has access.
    • If no transcript URL exists, it returns an empty array.
    • It fetches the transcript from the URL, parses it using jsonl-parse-stringify, and converts it into a structured format.
    • It populates speaker information (users and agents) by fetching their details from the database.
    • It maps the transcript items to include speaker information.
    • Finally, it returns the transcript with speaker details.
  2. Transcript Component (transcript.tsx):
    • The component is created to display the transcript.
    • It uses react-highlight-words for highlighting search terms.
    • It uses useQuery from tanstack-react-query to fetch the transcript data.
    • It includes a search input field to filter the transcript.
    • It iterates over the filtered transcript data and renders each item with the speaker's avatar, name, timestamp, and text.
    • The react-highlight-words component highlights the search terms within the transcript text.
    • Example: Searching for "math" highlights all occurrences of "math" in the transcript.
  3. Dependencies:
    • highlight-react
    • highlight-words
    • @types/react-highlight-words
  4. Code Snippets:
    • Parsing transcript from URL:
      const transcript = await fetch(existingMeeting.transcriptUrl).then(res => res.text()).then(text => jsonlParse(text, { type: streamTranscriptionItem }));
      
    • Highlighting search terms:
      <Highlighter
          highlightClassName="highlight"
          searchWords={[searchQuery]}
          autoEscape={true}
          textToHighlight={item.text}
      />
      

Implementing AI Chat Functionality

  1. Stream Chat Integration:
    • The stream-chat package is installed.
    • A stream-chat.ts file is created in the lib directory to manage Stream Chat initialization.
    • Environment variables for Stream Chat API key and secret are defined.
    • A generateChatToken procedure is created to generate a token for authenticating users with Stream Chat.
  2. Chat Provider Component (chat-provider.tsx):
    • This component acts as a wrapper for the chat UI.
    • It fetches the currently logged-in user's session.
    • It renders the ChatUI component, passing in the meeting ID, meeting name, user ID, username, and user image.
  3. Chat UI Component (chat-ui.tsx):
    • This component displays the chat interface.
    • It uses useCreateChatClient from stream-chat-react to initialize the Stream Chat client.
    • It uses useMutation to call the generateChatToken procedure and obtain a chat token.
    • It uses useEffect to connect to the chat channel when the client is initialized.
    • It renders the chat interface using components from stream-chat-react: Chat, Channel, Window, MessageList, MessageInput, and Thread.
  4. Dependencies:
    • stream-chat
    • stream-chat-react
  5. Code Snippets:
    • Initializing Stream Chat client:
      const client = useCreateChatClient({
          apiKey: process.env.NEXT_PUBLIC_STREAM_CHAT_API_KEY,
          generateToken: async () => {
              const { token } = await mutateAsync();
              return token;
          },
          });
      
    • Connecting to the chat channel:
      useEffect(() => {
          if (!client) return;
          const channel = client.channel('messaging', meetingId, {
              members: [userId],
          });
          setChannel(channel);
      }, [client, meetingId, userId]);
      

Integrating AI Agent Responses

  1. Webhook Implementation:
    • The Stream Chat webhook is used to listen for new messages.
    • When a new message is received, the webhook checks if the sender is not an agent.
    • If the sender is a user, the webhook retrieves the meeting summary and agent instructions.
    • It fetches the previous five messages from the chat to provide context to the AI agent.
    • It uses the OpenAI API to generate a response based on the meeting summary, agent instructions, and previous messages.
    • The AI agent's response is then sent to the chat channel.
  2. OpenAI Integration:
    • The openai package is installed.
    • The OpenAI API is used to generate responses based on the meeting summary, agent instructions, and previous messages.
    • The chatCompletions.create method is used to generate the AI agent's response.
  3. Code Snippets:
    • Generating GPT response:
      const gptResponse = await openai.chat.completions.create({
          messages: [
              { role: 'system', content: instructions },
              ...previousMessages,
              { role: 'user', content: text },
          ],
          model: 'gpt-3.5-turbo',
      });
      
    • Sending message to Stream Chat:
      await channel.sendMessage({
          text: gptResponseText,
          user: {
              id: existingAgent.id,
              name: existingAgent.name,
              image: generateAvatarUri({ seed: existingAgent.name }),
          },
      });
      

Implementing Payments with Polar.sh

Setting up Polar.sh

  1. Account Creation:
    • Create accounts on both polar.sh (production) and sandbox.polar.sh (development).
    • The sandbox environment is used for testing purposes.
  2. API Token Generation:
    • Generate a new API token in the sandbox environment with all scopes enabled and no expiration date.
    • Store the token in the environment variables as POLAR_ACCESS_TOKEN.
  3. Polar.sh SDK Integration:
    • Install the necessary Polar.sh packages: @polar.sh/sdk and @polar.sh/better-out.
    • Create a polar.ts file in the lib directory to initialize the Polar.sh client.
    • Configure the Polar.sh client with the API token and set the server type to sandbox.
  4. Better Out Plugin Integration:
    • Add the Polar.sh plugin to the Better Out configuration in al/util.ts.
    • Enable createCustomerOnSignUp to automatically create a Polar.sh customer when a new user signs up.
    • Use the checkout plugin to restrict checkout to authenticated users and redirect to /upgrade on successful purchase.
    • Use the portal plugin to enable access to the customer portal.
  5. Code Snippets:
    • Initializing Polar.sh client:
      import { Polar } from '@polar.sh/sdk';
      export const polarClient = new Polar({
          accessToken: process.env.POLAR_ACCESS_TOKEN,
          server: 'sandbox',
      });
      
    • Adding Polar.sh plugin to Better Out:
      plugins: [
          polar({
              client: polarClient,
              createCustomerOnSignUp: true,
              use: [
                  checkout({
                      authenticated: true,
                      successUrl: '/upgrade',
                  }),
                  portal(),
              ],
          }),
      ],
      

Implementing Free Tier Usage

  1. Premium Router:
    • Create a premium module with a server and procedures.
    • Create a getFreeUsage procedure to fetch the user's free tier usage.
    • The procedure retrieves the Polar.sh customer by external ID (user ID).
    • It checks if the customer has any active subscriptions. If so, it doesn't show free usage.
    • It counts the number of meetings and agents created by the user.
    • It returns the meeting count and agent count to the client.
  2. Dashboard Trial Component (dashboard-trial.tsx):
    • This component displays the user's free tier usage in the dashboard.
    • It uses useQuery to fetch the free usage data from the getFreeUsage procedure.
    • It displays the number of meetings and agents used, along with progress bars.
    • It includes a link to the /upgrade page.
  3. Premium Constants:
    • Create a constants.ts file in the premium module to define the maximum number of free meetings and agents.
  4. Premium Procedure:
    • Create a premiumProcedure in trpc/init.ts to enforce free tier limits.
    • This procedure extends the protected procedure and checks if the user has reached the free meeting or agent limit.
    • If the limit is reached, it throws a forbidden error.
  5. Code Snippets:
    • Fetching free usage data:
      const { data } = useQuery({
          queryKey: ['premium.getFreeUsage'],
          queryFn: () => trpc.premium.getFreeUsage.query(),
      });
      
    • Premium procedure implementation:
      const premiumProcedure = (entity: 'meetings' | 'agents') =>
          protectedProcedure.use(async ({ ctx, next }) => {
              // ... (code to check free tier limits)
              if (shouldThrowMeetingError) {
                  throw new TRPCError({
                      code: 'FORBIDDEN',
                      message: 'You have reached the maximum number of free meetings.',
                  });
              }
              // ... (similar code for agent error)
              return next({
                  ctx: {
                      customer,
                  },
              });
          });
      

Implementing the Upgrade Page

  1. Premium Router Procedures:
    • Add getProducts procedure to fetch all non-archived and recurring products from Polar.sh.
    • Add getCurrentSubscription procedure to fetch the user's current subscription from Polar.sh.
  2. Upgrade Page (upgrade/page.tsx):
    • This page displays the available subscription plans.
    • It uses prefetchQuery to prefetch the products and current subscription data on the server.
    • It uses useSuspenseQuery to fetch the products and current subscription data on the client.
    • It renders the UpgradeView component.
  3. Upgrade View Component (upgrade-view.tsx):
    • This component displays the available subscription plans.
    • It iterates over the products and renders a PricingCard component for each product.
    • It checks if the user is already subscribed to a product and displays the appropriate button text ("Manage" or "Change Plan").
    • It uses the checkout method from the Better Out plugin to open the Polar.sh checkout page.
  4. Pricing Card Component (pricing-card.tsx):
    • This component displays the details of a subscription plan.
    • It uses class-variance-authority to define different variants for the card (default and highlighted).
    • It displays the product title, description, price, features, and a call-to-action button.
  5. Code Snippets:
    • Fetching products and current subscription:
      const { data: products } = useSuspenseQuery({
          queryKey: ['premium.getProducts'],
          queryFn: () => trpc.premium.getProducts.query(),
      });
      const { data: currentSubscription } = useSuspenseQuery({
          queryKey: ['premium.getCurrentSubscription'],
          queryFn: () => trpc.premium.getCurrentSubscription.query(),
      });
      
    • Rendering the pricing card:
      <PricingCard
          key={product.id}
          variant={product.metadata?.variant as 'default' | 'highlighted' || 'default'}
          title={product.name}
          description={product.description}
          price={price}
          priceSuffix={`/${product.prices[0].recurring_interval}`}
          features={product.benefits.map((benefit) => benefit.description)}
          badge={product.metadata?.badge}
          buttonText={buttonText}
          onClick={onClick}
      />
      

Deployment and Bug Fixes

Preparing for Deployment

  1. Dashboard Command:
    • Implement the dashboard command to search for meetings and agents.
    • Use useRouter to navigate to the selected meeting or agent.
    • Use useQuery to fetch meetings and agents.
  2. Meeting Count:
    • Implement the proper meeting count for agents using database.$count.
    • Update the getMany and getOne procedures to include the meeting count.
  3. Cancel Meeting:
    • Remove the cancel meeting functionality as it doesn't provide much value to the user.
  4. Build Test:
    • Run npm run build to check for any errors before deployment.

Deployment Process

  1. Vercel Integration:
    • Add the project to Vercel and connect it with the GitHub repository.
    • Add the environment variables to the Vercel project.
    • Deploy the project to Vercel.
  2. Stream Configuration:
    • Update the Stream webhook URL to the new Vercel URL.
  3. Better Out URL:
    • Update the Better Out URL in the environment variables to the new Vercel URL.
  4. Authentication Configuration:
    • Update the GitHub and Google authentication settings with the new Vercel URL.
  5. Injest Integration:
    • Create an account with Injest and connect it with the Vercel project.
    • Enable Injest signing key and Injest event key.
    • Configure deployment protection bypass for Injest.
  6. Redirect from Homepage:
    • Add a redirect from the homepage to /meetings in next.config.js.

Bug Fixes

  1. Injest Fetch Error:
    • If the Injest step.fetch is failing, replace it with a normal fetch method.
    • Remove the response.ext.text transformation in the next step.
  2. NPM Install Error:
    • If there are NPM install errors during deployment, override the install command with npm install --legacy-peer-deps.

Conclusion

The YouTube video tutorial provides a comprehensive guide to building a real-time AI video call application with various advanced features. It covers everything from setting up authentication and database connections to implementing AI agents, searchable transcripts, AI chat, subscriptions, and deployment. The tutorial emphasizes the use of modern technologies and best practices, providing viewers with the knowledge and skills to create a sophisticated and feature-rich application. The detailed explanations, code snippets, and troubleshooting tips make it a valuable resource for developers looking to build similar applications.

Chat with this Video

AI-Powered

Hi! I can answer questions about this video "Build and Deploy a SaaS AI Agent Platform | Next.js 15, React, Better Auth, Polar | Part 2/2". What would you like to know?

Chat is based on the transcript of this video and may not be 100% accurate.

Related Videos

Ready to summarize another video?

Summarize YouTube Video