Lädt...


🔧 Building an Advanced AI Chatbot with React and window.ai


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

In this comprehensive tutorial, we'll walk through the process of creating a sophisticated AI-powered chatbot using React, TypeScript, and the window.ai API. This chatbot can handle queries from different sectors such as Engineering, Insurance, and Customer Service, providing a versatile and powerful tool for various applications.

Links

  1. Demo
  2. Source Code
  3. YouTube Video
  4. Improvements Commit Link

AI Chatbot Demo YouTube Video

AI Chatbot Demo

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Setting up the Project
  4. Defining TypeScript Interfaces
  5. Creating the AIChatBot Component
  6. Implementing Chat Logic
  7. Rendering the Chat Interface
  8. Adding Advanced Features
  9. Conclusion

Introduction

The AI Chatbot we're building is a React component that leverages the window.ai API to provide intelligent responses to user queries. It supports multiple sectors, manages conversation history, and offers a user-friendly interface for seamless interaction.

Prerequisites

Before we start, ensure you have:

  1. Node.js and npm installed on your machine
  2. Basic knowledge of React, TypeScript, and modern JavaScript features
  3. Google Chrome Dev or Canary browser (version 127 or higher)
  4. A code editor (e.g., Visual Studio Code)

Setting up the Project

Let's start by creating a new React project with TypeScript:

npm create vite@latest my-ai-chatbot --template react-ts
cd ai-chatbot

Follow this Shadcn UI Guide Shadcn UI Guide to install the Shadcn UI components.

Defining TypeScript Interfaces

First, let's define the TypeScript interfaces for our AI chatbot. Create a new file called ai.d.ts in your src folder:

// src/ai.d.ts

export interface AITextSessionOptions {
  maxTokens: number;
  temperature: number;
  tokensLeft: number;
  tokensSoFar: number;
  topK: number;
}

export interface AITextSession {
  prompt(input: string): Promise<string>;
  promptStreaming(input: string): AsyncIterable<string>;
  destroy(): void;
}

export interface Capabilities {
  available: "readily" | "after-download" | "no";
  defaultTemperature: number;
  defaultTopK: number;
  maxTopK: number;
}

declare global {
  interface Window {
    ai: {
      assistant: {
        capabilities: () => Promise<Capabilities>;
        create: (
          options?: Partial<AITextSessionOptions>
        ) => Promise<AITextSession>;
      };
    };
  }
}

These interfaces define the structure of the AI text session, its options, and the capabilities of the AI assistant. We also extend the global Window interface to include the ai property, which will be provided by the window.ai API.

Creating the AIChatBot Component

Now, let's create the main component for our AI chatbot. Create a new file called AIChatBot.tsx in your src folder:

// src/AIChatBot.tsx

import React, { useState, useEffect, useRef } from "react";
import {
  Button,
  Input,
  Card,
  CardContent,
  CardHeader,
  CardTitle,
  Avatar,
  AvatarFallback,
} from "./ui-components";
import { Popover, PopoverContent, PopoverTrigger } from "./ui-components";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "./ui-components";
import { ScrollArea } from "./ui-components";
import {
  UserIcon,
  SendIcon,
  XIcon,
  TrashIcon,
  EllipsisVertical,
  Clock,
  Bot,
  MessageCircle,
  FactoryIcon,
} from "lucide-react";

type Conversation = {
  message: string;
  isUser: boolean;
  isError?: boolean;
  id: string;
  date: string;
};

type AICapabilities = {
  isChrome: boolean;
  message: string;
  state?: {
    available: "readily" | "after-download" | "no";
  };
};

const sectors = [
  {
    name: "Engineering",
    description: "Technical support for engineering queries.",
  },
  {
    name: "Insurance",
    description: "Assistance with insurance-related queries.",
  },
  {
    name: "Customer Service",
    description: "General customer service support.",
  },
];

const AIChatBot: React.FC = () => {
  const [conversation, setConversation] = useState<Conversation[]>([]);
  const [capabilities, setCapabilities] = useState<AICapabilities>();
  const [message, setMessage] = useState("");
  const [isPending, setIsPending] = useState(false);
  const [sector, setSector] = useState("");
  const chatContainerRef = useRef<HTMLDivElement>(null);

  // ... We'll add more code here in the following sections
};

export default AIChatBot;

This sets up the basic structure of our AIChatBot component, including the necessary imports and type definitions.

Implementing Chat Logic

Now, let's add the core functionality to our chatbot:

// ... (previous code)

const AIChatBot: React.FC = () => {
  // ... (state variables)

  const generateId = () => crypto.randomUUID();

  const getCapabilities = async (): Promise<AICapabilities> => {
    try {
      let isChrome = false;
      let message = "";
      const state = await window.ai?.assistant.capabilities();

      if (navigator.userAgent.includes("Chrome")) {
        isChrome = (navigator as any).userAgentData?.brands.some(
          (brandInfo: { brand: string }) => brandInfo.brand === "Google Chrome"
        );
        if (!isChrome) {
          message = "Your browser is not supported. Please use Google Chrome Dev or Canary.";
        }
      } else {
        message = "Your browser is not supported. Please use Google Chrome Dev or Canary.";
      }

      const version = getChromeVersion();
      if (version < 127) {
        message = "Your browser is not supported. Please update to 127 version or greater.";
      }

      if (!("ai" in window)) {
        message = "Prompt API is not available, check your configuration in chrome://flags/#prompt-api-for-gemini-nano";
      }

      if (state?.available !== "readily") {
        message = "Built-in AI is not ready, check your configuration in chrome://flags/#optimization-guide-on-device-model";
      }

      return { isChrome, message, state };
    } catch (error) {
      console.error(error);
      return {
        isChrome: false,
        message: "An error occurred",
        state: undefined,
      };
    }
  };

  const getChromeVersion = () => {
    const raw = navigator.userAgent.match(/Chrom(e|ium)\\/([0-9]+)\\./);
    return raw ? parseInt(raw[2], 10) : 0;
  };

  const getPrompt = (message: string, oldConversations?: Conversation[]) => {
    return `
      You are a customer service representative and you are answering questions from our customers. Please follow the rules below and consider the customer's previous conversations to assist the customer.

      Previous conversations of the customer:

      \${oldConversations?.map(
        (conv) =>
          `\${conv.isUser ? "Customer Question: " : "Your Answer: "}\${
            conv.message
          }`
      )}

      1. Create your response using only HTML elements.
      2. You can use the following HTML tags to create your response: <p>, <h1>, <h2>, <h3>, <ul>, <li>, <strong>, <em>, <a>, <code>, <pre>, <img>.
      3. Do not use style or class attributes in your response.
      4. Create your response within a single root element, such as a <div> tag.
      5. Use the href attribute for links and use "#" instead of actual URLs.
      6. Respond to the customer's question politely, professionally, and helpfully.
      7. If you do not have information about the question asked, kindly state this and direct the customer to another resource that can help.

      Here is the customer's question:

      \${message}

      Please respond to this question in accordance with the rules above and finish the sentence.
    `;
  };

  const onFinish = async () => {
    if (!window?.ai || !message) return;
    const id = generateId();
    try {
      setIsPending(true);

      setConversation((prev) => [
        ...prev,
        { message, isUser: true, id, date: new Date().toISOString() },
      ]);

      const session = await window.ai.assistant.create();
      const prompt = getPrompt(message, conversation);
      const response = await session.prompt(prompt);

      setConversation((prev) => [
        ...prev,
        {
          message: response,
          isUser: false,
          id,
          date: new Date().toISOString(),
        },
      ]);

      session.destroy();
      setMessage("");
    } catch (error) {
      setConversation((prev) => [
        ...prev,
        {
          message: "An error occurred.",
          isUser: false,
          isError: true,
          id,
          date: new Date().toISOString(),
        },
      ]);
    } finally {
      setIsPending(false);
    }
  };

  useEffect(() => {
    const prepareCapabilities = () => {
      getCapabilities().then(setCapabilities);
    };

    prepareCapabilities();

    const interval = setInterval(prepareCapabilities, 60000); // Check every minute

    return () => clearInterval(interval);
  }, []);

  useEffect(() => {
    chatContainerRef.current?.scrollTo({
      top: chatContainerRef.current.scrollHeight,
      behavior: "smooth",
    });
  }, [conversation]);

  // ... (we'll add the return statement in the next section)
};

This section implements the core logic of our chatbot, including:

  • Generating unique IDs for messages
  • Checking the capabilities of the AI assistant
  • Creating prompts for the AI
  • Handling the chat interaction (sending messages and receiving responses)
  • Managing the conversation state
  • Automatically scrolling to the latest message

Rendering the Chat Interface

Now, let's add the JSX to render our chat interface:

// ... (previous code)

const AIChatBot: React.FC = () => {
  // ... (previous code)

  const handleDeleteAll = () => {
    setConversation([]);
    setSector("");
  };

  const getResponseTime = (id: string) => {
    const userMessage = conversation.find((conv) => conv.id === id);
    const aiMessage = conversation.find(
      (conv) => conv.id === id && !conv.isUser
    );
    if (!userMessage || !aiMessage) return;
    const userDate = new Date(userMessage.date);
    const aiDate = new Date(aiMessage.date);
    const diff = aiDate.getTime() - userDate.getTime();
    return ` - Response Time: \${diff}ms (\${diff / 1000}s)`;
  };

  const renderContent = () => {
    if (!sector) {
      return (
        <div className="flex flex-col items-center justify-center flex-1 h-full my-auto gap-y-4 min-h-[400px]">
          <MessageCircle className="w-12 h-12 text-gray-500" />
          <p className="px-4 text-sm text-center text-gray-500">
            Select a sector to start a conversation. Our AI Chatbot can assist
            you with various fields such as Engineering, Insurance, and Customer
            Service. Choose the relevant sector to get tailored support and
            solutions.
          </p>
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button variant="outline" size="sm" className="gap-x-2">
                <Bot className="w-5 h-5" />
                Select Sector
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent>
              {sectors.map((sector) => (
                <DropdownMenuItem
                  key={sector.name}
                  onClick={() => setSector(sector.name)}
                >
                  <FactoryIcon className="w-4 h-4 mr-2" />
                  <span>{sector.name}</span>
                </DropdownMenuItem>
              ))}
            </DropdownMenuContent>
          </DropdownMenu>
        </div>
      );
    }

    return (
      <>
        <ScrollArea className="h-[400px] px-2" viewportRef={chatContainerRef}>
          <div className="h-full space-y-4">
            {conversation?.length > 0 ? (
              conversation.map((item, index) => (
                <div
                  key={index}
                  className={`flex gap-2 \${
                    item.isUser ? "flex-row-reverse" : "flex-row"
                  }`}
                >
                  <Avatar>
                    <AvatarFallback>
                      {item.isUser ? <UserIcon /> : "AI"}
                    </AvatarFallback>
                  </Avatar>
                  <div>
                    <div
                      dangerouslySetInnerHTML={{ __html: item.message }}
                      className={`rounded-lg p-2 text-xs \${
                        item.isUser
                          ? "bg-primary text-primary-foreground"
                          : "bg-muted"
                      } \${
                        item.isError &&
                        "bg-destructive text-destructive-foreground"
                      }`}
                    />
                    {item.date && (
                      <div
                        title={new Date(item.date).toLocaleString()}
                        className={`flex items-center w-full mt-1 text-xs text-gray-500 \${
                          item.isUser ? "justify-end" : "justify-start"
                        }`}
                      >
                        <Clock className="inline-block w-4 h-4 mr-1" />
                        {new Date(item.date).toLocaleTimeString()}
                        {!item.isUser && getResponseTime(item.id)}
                      </div>
                    )}
                  </div>
                  {item.isError && (
                    <Button
                      variant="ghost"
                      size="icon"
                      onClick={() => {
                        setConversation((prev) =>
                          prev.filter((conv) => conv.id !== item.id)
                        );
                      }}
                    >
                      <TrashIcon className="w-4 h-4" />
                    </Button>
                  )}
                </div>
              ))
            ) : (
              <div className="flex flex-col items-center justify-center flex-1 h-full my-auto gap-y-4">
                <MessageCircle className="w-12 h-12 text-gray-500" />
                <p className="text-sm text-gray-500">
                  Start a conversation with AI Chatbot.
                </p>
              </div>
            )}
          </div>
        </ScrollArea>

        <div className="flex items-center p-2 space-x-2">
          <Input
            disabled={isPending}
            placeholder="Type your message"
            value={message}
            onChange={(e) => setMessage(e.target.value)}
            onKeyUp={(e) => e.key === "Enter" && onFinish()}
          />
          <DropdownMenu>
            <div className="flex flex-row ">
              <Button
                size="icon"
                variant="ghost"
                disabled={isPending || !message}
                onClick={onFinish}
              >
                <SendIcon className="w-4 h-4 text-primary hover:text-primary/90" />
              </Button>

              <DropdownMenuTrigger asChild>
                <Button size="icon" variant="ghost" disabled={isPending}>
                  <EllipsisVertical className="w-4 h-4" />
                </Button>
              </DropdownMenuTrigger>
            </div>
            <DropdownMenuContent>
              <DropdownMenuItem
                disabled={isPending || conversation.length === 0}
                onClick={handleDeleteAll}
              >
                <TrashIcon className="w-4 h-4 mr-2" />
                <span>Delete all messages</span>
              </DropdownMenuItem>
              <DropdownMenuItem disabled={isPending} onClick={handleDeleteAll}>
                <FactoryIcon className="w-4 h-4 mr-2" />
                <span>Change sector</span>
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </div>
      </>
    );
  };

  return (
    <Card className="w-[400px] overflow-hidden">
      <CardHeader className="flex flex-row items-center justify-between p-2 m-2 space-y-0 rounded-lg bg-primary">
        <CardTitle className="text-sm font-medium text-white dark:text-black">
          AI Chatbot
        </CardTitle>
        <Button
          variant="ghost"
          size="sm"
          onClick={() => {
            /* Close function */
          }}
          className="text-white dark:text-black dark:hover:text-white"
        >
          <XIcon className="w-4 h-4" />
        </Button>
      </CardHeader>
      <CardContent className="p-0 mb-2">{renderContent()}</CardContent>
    </Card>
  );
};

export default AIChatBot;

This section renders the chat interface, including:

  • A dropdown to select the sector
  • The conversation history with user and AI messages
  • An input field for the user to type messages
  • Buttons to send messages, delete the conversation, and change sectors

Adding Advanced Features

To make our chatbot more robust and user-friendly, we can add some advanced features:

  1. Error handling and retry mechanism
  2. Typing indicators
  3. Message reactions
  4. File uploads

Here's an example of how we could implement a typing indicator:

// Add this to your imports
import { Loader } from "lucide-react";

// Add this state variable
const [isTyping, setIsTyping] = useState(false);

// Modify the onFinish function
const onFinish = async () => {
  if (!window?.ai || !message) return;
  const id = generateId();
  try {
    setIsPending(true);
    setIsTyping(true);

    setConversation((prev) => [
      ...prev,
      { message, isUser: true, id, date: new Date().toISOString() },
    ]);

    const session = await window.ai.assistant.create();
    const prompt = getPrompt(message, conversation);
    const response = await session.prompt(prompt);

    setIsTyping(false);

    setConversation((prev) => [
      ...prev,
      {
        message: response,
        isUser: false,
        id,
        date: new Date().toISOString(),
      },
    ]);

    session.destroy();
    setMessage("");
  } catch (error) {
    setIsTyping(false);
    setConversation((prev) => [
      ...prev,
      {
        message: "An error occurred.",
        isUser: false,
        isError: true,
        id,
        date: new Date().toISOString(),
      },
    ]);
  } finally {
    setIsPending(false);
  }
};

// Add this to your renderContent function, just before the closing ScrollArea tag
{
  isTyping && (
    <div className="flex items-center space-x-2 text-gray-500">
      <Loader className="w-4 h-4 animate-spin" />
      <span>AI is typing...</span>
    </div>
  );
}

This addition will show a typing indicator while the AI is generating a response, providing better feedback to the user.

Conclusion

In this tutorial, we've built a sophisticated AI Chatbot using React, TypeScript, and the window.ai API. Our chatbot can handle multiple sectors, maintain conversation history, and provide a user-friendly interface for interaction.

Some potential improvements and extensions could include:

  1. Implementing user authentication
  2. Adding support for voice input and output
  3. Integrating with a backend to store conversation history
  4. Implementing more advanced AI features like sentiment analysis or language translation

Remember to thoroughly test your chatbot and ensure it complies with all relevant privacy and data protection regulations before deploying it in a production environment.

Happy coding!

...

🔧 Building an Advanced AI Chatbot with React and window.ai


📈 40.07 Punkte
🔧 Programmierung

🔧 Building CRUD App with react-form, zod, react data grid, react-query and json-server


📈 25.28 Punkte
🔧 Programmierung

🔧 This Week In React #185: React Conf, React Query, refs, Next.js after, mini-react...


📈 23.93 Punkte
🔧 Programmierung

🔧 This Week In React #185: React Conf, React Query, refs, Next.js after, mini-react...


📈 23.93 Punkte
🔧 Programmierung

🔧 Virtual Scrolling in React: Implementation from scratch and using react-window


📈 23.58 Punkte
🔧 Programmierung

🔧 Building Your Own AI Chatbot With React and ChatGPT API


📈 22.77 Punkte
🔧 Programmierung

📰 GTK+ Implements Window Focus Tracking and Window Properties for Ubuntu's Mir


📈 21.93 Punkte
📰 IT Security Nachrichten

🐧 Are there other old-fashioned window managers than Window Maker?


📈 20.62 Punkte
🐧 Linux Tipps

🕵️ Apple Safari 2.0.4 419.3 window.console.log() window.console.log denial of service


📈 20.62 Punkte
🕵️ Sicherheitslücken

🕵️ Mozilla Firefox up to 2.0.4 window.print(window.print) denial of service


📈 20.62 Punkte
🕵️ Sicherheitslücken

🍏 How To Merge a Single Page Safari Window With Another Safari Window?


📈 20.62 Punkte
🍏 iOS / Mac OS

🐧 Can you make a window float in a tiling window manager (those that are not dynamic)?


📈 20.62 Punkte
🐧 Linux Tipps

🔧 Creating A Window Virtual Machine, RDP into it, Add a data disc to window virtual machine.


📈 20.62 Punkte
🔧 Programmierung

🔧 Building A Scalable Advanced Email Templating System with @react-email and NestJS


📈 20.31 Punkte
🔧 Programmierung

🔧 Building a CRUD App with Next.js, React Query, React Hook Form, and Yup


📈 19.3 Punkte
🔧 Programmierung

🔧 Building Offline-First Apps using React Native, React Query, and AsyncStorage


📈 19.3 Punkte
🔧 Programmierung

🔧 Building React Forms with Ease Using React Hook Form, Zod and Shadcn


📈 19.3 Punkte
🔧 Programmierung

🔧 What’s New in React 19? React Canaries, Actions, and React Compiler


📈 19.26 Punkte
🔧 Programmierung

🔧 Dynamic Manipulation of Children in React JSX with React.cloneElement and React.Children.map


📈 19.26 Punkte
🔧 Programmierung

🔧 Learn React Router v6: Building a Feature-Rich Blog Application with Advanced Routing Techniques


📈 19 Punkte
🔧 Programmierung

🔧 Building My Own React-Like Library with Advanced Features! 🚀


📈 19 Punkte
🔧 Programmierung

🔧 Advanced LangGraph: Building Intelligent Agents with ReACT Architecture


📈 19 Punkte
🔧 Programmierung

🔧 React with TypeScript: Advanced Techniques (Compatible with React 19) 🚀


📈 18.96 Punkte
🔧 Programmierung

🔧 React with TypeScript: Advanced Techniques (Compatible with React 19) 🚀


📈 18.96 Punkte
🔧 Programmierung

🔧 Build a Production-Ready, Intelligent Chatbot With the MongoDB Chatbot Framework


📈 18.91 Punkte
🔧 Programmierung

🔧 Building and Deploying a Chatbot With Google Cloud Run and Dialogflow


📈 18.1 Punkte
🔧 Programmierung

🔧 Building an Effective and User-Friendly Medical Chatbot with OpenAI and CometLLM: A Step-by-Step Guide


📈 18.1 Punkte
🔧 Programmierung

🔧 Building Pagination in React with React Paginate


📈 17.99 Punkte
🔧 Programmierung

matomo