logoassistant-ui

ToolFallback

Overview

The ToolFallback component displays a default ToolUI for tools that do not have a dedicated ToolUI.

Getting Started

Add tool-fallback

npx shadcn@latest add @assistant-ui/tool-fallback

Main Component

npm install @assistant-ui/react
yarn add @assistant-ui/react
pnpm add @assistant-ui/react
bun add @assistant-ui/react
xpm add @assistant-ui/react
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";import {  CheckIcon,  ChevronDownIcon,  ChevronUpIcon,  XCircleIcon,} from "lucide-react";import { useState } from "react";import { Button } from "@/components/ui/button";import { cn } from "@/lib/utils";export const ToolFallback: ToolCallMessagePartComponent = ({  toolName,  argsText,  result,  status,}) => {  const [isCollapsed, setIsCollapsed] = useState(true);  const isCancelled =    status?.type === "incomplete" && status.reason === "cancelled";  const cancelledReason =    isCancelled && status.error      ? typeof status.error === "string"        ? status.error        : JSON.stringify(status.error)      : null;  return (    <div      className={cn(        "aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",        isCancelled && "border-muted-foreground/30 bg-muted/30",      )}    >      <div className="aui-tool-fallback-header flex items-center gap-2 px-4">        {isCancelled ? (          <XCircleIcon className="aui-tool-fallback-icon size-4 text-muted-foreground" />        ) : (          <CheckIcon className="aui-tool-fallback-icon size-4" />        )}        <p          className={cn(            "aui-tool-fallback-title grow",            isCancelled && "text-muted-foreground line-through",          )}        >          {isCancelled ? "Cancelled tool: " : "Used tool: "}          <b>{toolName}</b>        </p>        <Button onClick={() => setIsCollapsed(!isCollapsed)}>          {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}        </Button>      </div>      {!isCollapsed && (        <div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">          {cancelledReason && (            <div className="aui-tool-fallback-cancelled-root px-4">              <p className="aui-tool-fallback-cancelled-header font-semibold text-muted-foreground">                Cancelled reason:              </p>              <p className="aui-tool-fallback-cancelled-reason text-muted-foreground">                {cancelledReason}              </p>            </div>          )}          <div            className={cn(              "aui-tool-fallback-args-root px-4",              isCancelled && "opacity-60",            )}          >            <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">              {argsText}            </pre>          </div>          {!isCancelled && result !== undefined && (            <div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">              <p className="aui-tool-fallback-result-header font-semibold">                Result:              </p>              <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">                {typeof result === "string"                  ? result                  : JSON.stringify(result, null, 2)}              </pre>            </div>          )}        </div>      )}    </div>  );};

shadcn/ui dependencies

npm install @radix-ui/react-slot
yarn add @radix-ui/react-slot
pnpm add @radix-ui/react-slot
bun add @radix-ui/react-slot
xpm add @radix-ui/react-slot
import * as React from "react";import { Slot } from "@radix-ui/react-slot";import { cva, type VariantProps } from "class-variance-authority";import { cn } from "@/lib/utils";const buttonVariants = cva(  "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm no-underline outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",  {    variants: {      variant: {        default:          "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",        destructive:          "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",        outline:          "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",        secondary:          "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",        ghost:          "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",        link: "text-primary underline-offset-4 hover:underline",      },      size: {        default: "h-9 px-4 py-2 has-[>svg]:px-3",        sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",        icon: "size-9",      },    },    defaultVariants: {      variant: "default",      size: "default",    },  },);function Button({  className,  variant,  size,  asChild = false,  ...props}: React.ComponentProps<"button"> &  VariantProps<typeof buttonVariants> & {    asChild?: boolean;  }) {  const Comp = asChild ? Slot : "button";  return (    <Comp      data-slot="button"      className={cn(buttonVariants({ variant, size, className }))}      {...props}    />  );}export { Button, buttonVariants };

This adds a /components/assistant-ui/tool-fallback.tsx file to your project, which you can adjust as needed.

Use it in your application

Pass the ToolFallback component to the MessagePrimitive.Parts component

/components/assistant-ui/thread.tsx
import {  } from "@/components/assistant-ui/tool-fallback";

const :  = () => {
  return (
    <. ="...">
      < ="...">
        <.
          ={{ : { :  } }}
        />
      </>
      < />

      < ="..." />
    </.>
  );
};

On this page

Edit on GitHub