From dcb56a07d9a9f0d61423e57e44ef340b8b54ff8e Mon Sep 17 00:00:00 2001
From: Minwook Shin <163576506+minwookshin@users.noreply.github.com>
Date: Wed, 16 Sep 2026 22:26:16 -0400
Subject: [PATCH] fix(tool): handle undefined input while streaming
---
.../content/components/(chatbot)/tool.mdx | 2 +-
packages/elements/__tests__/tool.test.tsx | 68 +++++++++++++++++++
packages/elements/src/tool.tsx | 24 ++++---
packages/examples/src/tool.tsx | 2 +-
4 files changed, 85 insertions(+), 11 deletions(-)
diff --git a/apps/docs/content/components/(chatbot)/tool.mdx b/apps/docs/content/components/(chatbot)/tool.mdx
index a56f31c4..27fca941 100644
--- a/apps/docs/content/components/(chatbot)/tool.mdx
+++ b/apps/docs/content/components/(chatbot)/tool.mdx
@@ -277,7 +277,7 @@ Shows a tool that encountered an error during execution. Opens by default to dis
type={{
input: {
description:
- "The input parameters passed to the tool, displayed as formatted JSON.",
+ "The input parameters passed to the tool, displayed as formatted JSON. Renders nothing when input is undefined, such as before parameters arrive during input-streaming.",
type: 'ToolUIPart["input"]',
},
"...props": {
diff --git a/packages/elements/__tests__/tool.test.tsx b/packages/elements/__tests__/tool.test.tsx
index a7225762..e47a6dbf 100644
--- a/packages/elements/__tests__/tool.test.tsx
+++ b/packages/elements/__tests__/tool.test.tsx
@@ -10,6 +10,19 @@ import {
const QUERY_REGEX = /"query"/;
+const renderTool = (
+ input: unknown,
+ state: "input-streaming" | "input-available"
+) => (
+
+
+
+
+
+
+
+);
+
describe("tool", () => {
it("renders children", () => {
render(Content);
@@ -182,6 +195,61 @@ describe("toolContent", () => {
});
describe("toolInput", () => {
+ it("renders nothing when input is undefined", () => {
+ const { container } = render();
+
+ expect(container).toBeEmptyDOMElement();
+ });
+
+ it("keeps the tool visible as input starts streaming and becomes available", () => {
+ const { container, rerender } = render(
+ renderTool(undefined, "input-streaming")
+ );
+
+ expect(
+ screen.getByRole("button", { name: "searchPending" })
+ ).toBeInTheDocument();
+ expect(screen.queryByText("Parameters")).not.toBeInTheDocument();
+
+ rerender(renderTool({ query: "test" }, "input-streaming"));
+
+ expect(container.querySelector("code")).toHaveTextContent('"test"');
+
+ rerender(renderTool({ query: "test search" }, "input-available"));
+
+ expect(
+ screen.getByRole("button", { name: "searchRunning" })
+ ).toBeInTheDocument();
+ expect(container.querySelector("code")).toHaveTextContent('"test search"');
+ });
+
+ it("removes previous parameters when a new input starts streaming", () => {
+ const { container, rerender } = render(
+ renderTool({ query: "test search" }, "input-available")
+ );
+
+ rerender(renderTool(undefined, "input-streaming"));
+
+ expect(
+ screen.getByRole("button", { name: "searchPending" })
+ ).toBeInTheDocument();
+ expect(screen.queryByText("Parameters")).not.toBeInTheDocument();
+ expect(container.querySelector("code")).not.toBeInTheDocument();
+ });
+
+ it.each([
+ { input: null, json: "null" },
+ { input: false, json: "false" },
+ { input: 0, json: "0" },
+ { input: "", json: '""' },
+ { input: {}, json: "{}" },
+ ])("renders defined input $json", ({ input, json }) => {
+ const { container } = render();
+
+ expect(screen.getByText("Parameters")).toBeInTheDocument();
+ expect(container.querySelector("code")).toHaveTextContent(json);
+ });
+
it("renders input parameters", async () => {
const input = { query: "test search" };
render(
diff --git a/packages/elements/src/tool.tsx b/packages/elements/src/tool.tsx
index 9a22010e..3474453c 100644
--- a/packages/elements/src/tool.tsx
+++ b/packages/elements/src/tool.tsx
@@ -116,16 +116,22 @@ export type ToolInputProps = ComponentProps<"div"> & {
input: ToolPart["input"];
};
-export const ToolInput = ({ className, input, ...props }: ToolInputProps) => (
-
-
- Parameters
-
-
-
+export const ToolInput = ({ className, input, ...props }: ToolInputProps) => {
+ if (input === undefined) {
+ return null;
+ }
+
+ return (
+
-
-);
+ );
+};
export type ToolOutputProps = ComponentProps<"div"> & {
output: ToolPart["output"];
diff --git a/packages/examples/src/tool.tsx b/packages/examples/src/tool.tsx
index d24d71aa..21323db1 100644
--- a/packages/examples/src/tool.tsx
+++ b/packages/examples/src/tool.tsx
@@ -57,7 +57,7 @@ const Example = () => (
type="tool-database_query"
/>
-
+