/* Copyright 2026 Marimo. All rights reserved. */ import type { ToolUIPart } from "ai"; import { isEmpty } from "lodash-es"; import { CheckCircleIcon, InfoIcon, Loader2, WrenchIcon, XCircleIcon, } from "lucide-react"; import React from "react"; import { z } from "zod"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { cn } from "@/utils/cn"; // Zod schema matching the Python SuccessResult dataclass const SuccessResultSchema = z .object({ status: z.string().default("success"), auth_required: z.boolean().default(false), action_url: z.any(), next_steps: z.any(), meta: z.any(), message: z.string().nullish(), }) .passthrough(); type SuccessResult = z.infer; const PrettySuccessResult: React.FC<{ data: SuccessResult }> = ({ data }) => { const { status, auth_required, action_url: _action_url, meta: _meta, next_steps: _next_steps, message, ...rest } = data; return (

Tool Result

{status} {auth_required && ( Auth Required )}
{/* Message */} {message && (
{message}
)} {/* Data */} {rest && (
{Object.entries(rest).map(([key, value]) => { if (isEmpty(value)) { return null; } return (
{key}
                  {JSON.stringify(value, null, 2)}
                
); })}
)}
); }; const ResultRenderer: React.FC<{ result: unknown }> = ({ result }) => { // Try to parse the result with our Zod schema const parseResult = SuccessResultSchema.safeParse(result); if (parseResult.success) { // If it matches the SuccessResult schema, show the pretty UI return ; } // Otherwise, fall back to the current JSON viewer return (
{typeof result === "string" ? result : JSON.stringify(result, null, 2)}
); }; const ToolArgsRenderer: React.FC<{ input: unknown }> = ({ input }) => { const hasinput = input && input !== null; if (!hasinput) { return null; } const isEmptyInput = isEmpty(input); const isObject = typeof input === "object" && !Array.isArray(input) && Object.keys(input as Record).length > 0; return (

Tool Request

        {isEmptyInput
          ? "{}"
          : isObject
            ? JSON.stringify(input, null, 2)
            : String(input)}
      
); }; interface ToolCallAccordionProps { toolName: string; result: unknown; error?: string; index?: number; state?: ToolUIPart["state"]; className?: string; input?: unknown; } export const ToolCallAccordion: React.FC = ({ toolName, result, error, index = 0, state, className, input, }) => { const hasResult = state === "output-available" && (result || error); const status = error ? "error" : hasResult ? "success" : "loading"; const getStatusIcon = () => { switch (status) { case "loading": return ; case "error": return ; case "success": return ; default: return ; } }; const getStatusText = () => { if (status === "loading") { return "Running"; } if (error) { return "Failed"; } if (hasResult) { return "Done"; } return "Tool call"; }; return ( svg]:rotate-180 hover:no-underline", status === "error" && "text-[var(--red-11)]/80", status === "success" && "text-[var(--grass-11)]/80", )} > {getStatusIcon()} {getStatusText()}: {formatToolName(toolName)} {/* Only show content when tool is complete */} {hasResult && (
{result !== undefined && result !== null && ( )} {/* Error */} {error && (
Error
{error}
)}
)} ); }; function formatToolName(toolName: string) { return toolName.replace("tool-", ""); }