Merge pull request #52 from langchain-ai/brace/hide-tool-calls

feat: add option to hide tool calls
This commit is contained in:
Brace Sproul
2025-03-11 13:26:34 -07:00
committed by GitHub
5 changed files with 159 additions and 49 deletions

View File

@@ -18,15 +18,16 @@
"@assistant-ui/react-markdown": "^0.8.0",
"@assistant-ui/react-syntax-highlighter": "^0.7.2",
"@langchain/core": "^0.3.41",
"@langchain/langgraph": "^0.2.49",
"@langchain/langgraph-api": "^0.0.14",
"@langchain/langgraph-cli": "^0.0.14",
"@langchain/langgraph-sdk": "^0.0.52",
"@langchain/langgraph": "^0.2.54",
"@langchain/langgraph-api": "^0.0.15",
"@langchain/langgraph-cli": "^0.0.15",
"@langchain/langgraph-sdk": "^0.0.53",
"@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.8",
"@tailwindcss/postcss": "^4.0.9",
"@tailwindcss/vite": "^4.0.9",

108
pnpm-lock.yaml generated
View File

@@ -20,17 +20,17 @@ importers:
specifier: ^0.3.41
version: 0.3.41(openai@4.85.4(zod@3.24.2))
"@langchain/langgraph":
specifier: ^0.2.49
version: 0.2.49(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
specifier: ^0.2.54
version: 0.2.54(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
"@langchain/langgraph-api":
specifier: ^0.0.14
version: 0.0.14(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.49(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)
specifier: ^0.0.15
version: 0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.16(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.54(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)
"@langchain/langgraph-cli":
specifier: ^0.0.14
version: 0.0.14(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.49(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)
specifier: ^0.0.15
version: 0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.16(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.54(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)
"@langchain/langgraph-sdk":
specifier: ^0.0.52
version: 0.0.52(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
specifier: ^0.0.53
version: 0.0.53(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
"@radix-ui/react-avatar":
specifier: ^1.1.3
version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -46,6 +46,9 @@ importers:
"@radix-ui/react-slot":
specifier: ^1.1.2
version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
"@radix-ui/react-switch":
specifier: ^1.1.3
version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
"@radix-ui/react-tooltip":
specifier: ^1.1.8
version: 1.1.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -846,10 +849,10 @@ packages:
}
engines: { node: ">=18" }
"@langchain/langgraph-api@0.0.14":
"@langchain/langgraph-api@0.0.15":
resolution:
{
integrity: sha512-/lh6ug9kXBhL5zrX56MA4xxNt99kzLQqNuYqQRd2PWflVNATMRJNMfWhLjh91Hbn0yf3CWQoIX/6mPQiwCfrKg==,
integrity: sha512-cehdr2QsgDFEUjo0L2gBVT96M7YNXPQWywNF/bEsIgDBPfEB7qvjNmWD7d8diCqVyKSFZ0+qDXMflJhmBshomQ==,
}
engines: { node: ^18.19.0 || >=20.16.0 }
peerDependencies:
@@ -858,27 +861,27 @@ packages:
"@langchain/langgraph-checkpoint": ^0.0.15
typescript: ^5.5.4
"@langchain/langgraph-checkpoint@0.0.15":
"@langchain/langgraph-checkpoint@0.0.16":
resolution:
{
integrity: sha512-AiJkvsYHqNbCh1Tx823qs2lf2qRqeB4EAMejirOk8gkpPszAGYua5c3niKYkcKR2tU8Snhrmj7Gm9HKZSFOXyw==,
integrity: sha512-B50l7w9o9353drHsdsD01vhQrCJw0eqvYeXid7oKeoj1Yye+qY90r97xuhiflaYCZHM5VEo2oaizs8oknerZsQ==,
}
engines: { node: ">=18" }
peerDependencies:
"@langchain/core": ">=0.2.31 <0.4.0"
"@langchain/langgraph-cli@0.0.14":
"@langchain/langgraph-cli@0.0.15":
resolution:
{
integrity: sha512-wB6Q1VjAspGUXfbZnNuq56lXQNHHedqN09nfpGxNQnfnCf8VW/8veSkhCaNV5gdvRV9mDAWhJ0i78gxLxPhbRw==,
integrity: sha512-xbVW4UGyRJOG95uWE02IdM+Uik8qNtgiIJ9R2W/G7smqc+15aXK1UIYfKRBIGnsnnEftxPUEcMat/R9w6SGUMw==,
}
engines: { node: ^18.19.0 || >=20.16.0 }
hasBin: true
"@langchain/langgraph-sdk@0.0.52":
"@langchain/langgraph-sdk@0.0.53":
resolution:
{
integrity: sha512-nPHm9trQJnRxUDWVl0LCZ0FrQu22RtnamTkrlNibTxpcpI8E3d6KxGxzwYqLgs+hQVyJXjCb6pTNSgahaPaR5g==,
integrity: sha512-Do4coXHCMvWXRfY76nN69U03uB5kz1fZbfmEjAFAtclLcE2B5BTpQ6Zb8ya9jRuft4MvLWjyP1N+0M9ExaqIKA==,
}
peerDependencies:
"@langchain/core": ">=0.2.31 <0.4.0"
@@ -889,10 +892,10 @@ packages:
react:
optional: true
"@langchain/langgraph@0.2.49":
"@langchain/langgraph@0.2.54":
resolution:
{
integrity: sha512-CI8M8gz5bp6z1EcrcbKfHzZRGGHF8vYjih6vQTW7t3FEQAYzrLdpE4c7tw/jr7n2/9rb/bUkM7CL9pswUDoZfQ==,
integrity: sha512-+P2rU0Qz6bBCNPXOSV8WeUpLRTvhu8fQuzMYR2MqWsbbfmZrfmLxqtVWPHkmr5khx/txxFy1vOBAy+KwZ94mrg==,
}
engines: { node: ">=18" }
peerDependencies:
@@ -1184,6 +1187,22 @@ packages:
"@types/react":
optional: true
"@radix-ui/react-switch@1.1.3":
resolution:
{
integrity: sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ==,
}
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
"@radix-ui/react-tooltip@1.1.8":
resolution:
{
@@ -1248,6 +1267,18 @@ packages:
"@types/react":
optional: true
"@radix-ui/react-use-previous@1.1.0":
resolution:
{
integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==,
}
peerDependencies:
"@types/react": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@radix-ui/react-use-rect@1.1.0":
resolution:
{
@@ -5895,14 +5926,14 @@ snapshots:
transitivePeerDependencies:
- openai
"@langchain/langgraph-api@0.0.14(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.49(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)":
"@langchain/langgraph-api@0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.16(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.54(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)":
dependencies:
"@babel/code-frame": 7.26.2
"@hono/node-server": 1.13.8(hono@4.7.2)
"@hono/zod-validator": 0.2.2(hono@4.7.2)(zod@3.24.2)
"@langchain/core": 0.3.41(openai@4.85.4(zod@3.24.2))
"@langchain/langgraph": 0.2.49(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
"@langchain/langgraph-checkpoint": 0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))
"@langchain/langgraph": 0.2.54(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
"@langchain/langgraph-checkpoint": 0.0.16(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))
"@types/json-schema": 7.0.15
"@typescript/vfs": 1.6.1(typescript@5.7.3)
dedent: 1.5.3
@@ -5926,16 +5957,16 @@ snapshots:
- openai
- supports-color
"@langchain/langgraph-checkpoint@0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))":
"@langchain/langgraph-checkpoint@0.0.16(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))":
dependencies:
"@langchain/core": 0.3.41(openai@4.85.4(zod@3.24.2))
uuid: 10.0.0
"@langchain/langgraph-cli@0.0.14(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.49(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)":
"@langchain/langgraph-cli@0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.16(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.54(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)":
dependencies:
"@babel/code-frame": 7.26.2
"@commander-js/extra-typings": 13.1.0(commander@13.1.0)
"@langchain/langgraph-api": 0.0.14(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.49(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)
"@langchain/langgraph-api": 0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(@langchain/langgraph-checkpoint@0.0.16(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))))(@langchain/langgraph@0.2.54(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0))(openai@4.85.4(zod@3.24.2))(typescript@5.7.3)
chokidar: 4.0.3
commander: 13.1.0
dedent: 1.5.3
@@ -5960,7 +5991,7 @@ snapshots:
- supports-color
- typescript
"@langchain/langgraph-sdk@0.0.52(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)":
"@langchain/langgraph-sdk@0.0.53(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)":
dependencies:
"@types/json-schema": 7.0.15
p-queue: 6.6.2
@@ -5970,11 +6001,11 @@ snapshots:
"@langchain/core": 0.3.41(openai@4.85.4(zod@3.24.2))
react: 19.0.0
"@langchain/langgraph@0.2.49(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)":
"@langchain/langgraph@0.2.54(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)":
dependencies:
"@langchain/core": 0.3.41(openai@4.85.4(zod@3.24.2))
"@langchain/langgraph-checkpoint": 0.0.15(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))
"@langchain/langgraph-sdk": 0.0.52(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
"@langchain/langgraph-checkpoint": 0.0.16(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))
"@langchain/langgraph-sdk": 0.0.53(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
uuid: 10.0.0
zod: 3.24.2
transitivePeerDependencies:
@@ -6184,6 +6215,21 @@ snapshots:
optionalDependencies:
"@types/react": 19.0.10
"@radix-ui/react-switch@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)":
dependencies:
"@radix-ui/primitive": 1.1.1
"@radix-ui/react-compose-refs": 1.1.1(@types/react@19.0.10)(react@19.0.0)
"@radix-ui/react-context": 1.1.1(@types/react@19.0.10)(react@19.0.0)
"@radix-ui/react-primitive": 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
"@radix-ui/react-use-controllable-state": 1.1.0(@types/react@19.0.10)(react@19.0.0)
"@radix-ui/react-use-previous": 1.1.0(@types/react@19.0.10)(react@19.0.0)
"@radix-ui/react-use-size": 1.1.0(@types/react@19.0.10)(react@19.0.0)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
optionalDependencies:
"@types/react": 19.0.10
"@types/react-dom": 19.0.4(@types/react@19.0.10)
"@radix-ui/react-tooltip@1.1.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)":
dependencies:
"@radix-ui/primitive": 1.1.1
@@ -6230,6 +6276,12 @@ snapshots:
optionalDependencies:
"@types/react": 19.0.10
"@radix-ui/react-use-previous@1.1.0(@types/react@19.0.10)(react@19.0.0)":
dependencies:
react: 19.0.0
optionalDependencies:
"@types/react": 19.0.10
"@radix-ui/react-use-rect@1.1.0(@types/react@19.0.10)(react@19.0.0)":
dependencies:
"@radix-ui/rect": 1.1.0

View File

@@ -25,6 +25,8 @@ import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
import ThreadHistory from "./history";
import { toast } from "sonner";
import { useMediaQuery } from "@/hooks/useMediaQuery";
import { Label } from "../ui/label";
import { Switch } from "../ui/switch";
function StickyToBottomContent(props: {
content: ReactNode;
@@ -70,6 +72,10 @@ export function Thread() {
"chatHistoryOpen",
BooleanParam,
);
const [hideToolCalls, setHideToolCalls] = useQueryParam(
"hideToolCalls",
BooleanParam,
);
const [input, setInput] = useState("");
const [firstTokenReceived, setFirstTokenReceived] = useState(false);
const isLargeScreen = useMediaQuery("(min-width: 1024px)");
@@ -335,7 +341,22 @@ export function Thread() {
className="p-3.5 pb-0 border-none bg-transparent field-sizing-content shadow-none ring-0 outline-none focus:outline-none focus:ring-0 resize-none"
/>
<div className="flex items-center justify-end p-2 pt-0">
<div className="flex items-center justify-between p-2 pt-4">
<div>
<div className="flex items-center space-x-2">
<Switch
id="render-tool-calls"
checked={hideToolCalls ?? false}
onCheckedChange={setHideToolCalls}
/>
<Label
htmlFor="render-tool-calls"
className="text-sm text-gray-600"
>
Hide Tool Calls
</Label>
</div>
</div>
{stream.isLoading ? (
<Button key="stop" onClick={() => stream.stop()}>
<LoaderCircle className="w-4 h-4 animate-spin" />

View File

@@ -11,6 +11,7 @@ import { MessageContentComplex } from "@langchain/core/messages";
import { Fragment } from "react/jsx-runtime";
import { isAgentInboxInterruptSchema } from "@/lib/agent-inbox-interrupt";
import { ThreadView } from "../agent-inbox";
import { BooleanParam, useQueryParam } from "use-query-params";
function CustomComponent({
message,
@@ -19,15 +20,10 @@ function CustomComponent({
message: Message;
thread: ReturnType<typeof useStreamContext>;
}) {
const meta = thread.getMessagesMetadata(message);
const seenState = meta?.firstSeenState;
const customComponents = seenState?.values.ui
?.slice()
.filter(({ additional_kwargs }) =>
!additional_kwargs.message_id
? additional_kwargs.run_id === seenState.metadata?.run_id
: additional_kwargs.message_id === message.id,
);
const { values } = useStreamContext();
const customComponents = values.ui?.filter(
(ui) => ui.metadata?.message_id === message.id,
);
if (!customComponents?.length) return null;
return (
@@ -78,6 +74,7 @@ export function AssistantMessage({
handleRegenerate: (parentCheckpoint: Checkpoint | null | undefined) => void;
}) {
const contentString = getContentString(message.content);
const [hideToolCalls] = useQueryParam("hideToolCalls", BooleanParam);
const thread = useStreamContext();
const isLastMessage =
@@ -101,6 +98,10 @@ export function AssistantMessage({
const hasAnthropicToolCalls = !!anthropicStreamedToolCalls?.length;
const isToolResult = message.type === "tool";
if (isToolResult && hideToolCalls) {
return null;
}
return (
<div className="flex items-start mr-auto gap-2 group">
{isToolResult ? (
@@ -112,13 +113,19 @@ export function AssistantMessage({
<MarkdownText>{contentString}</MarkdownText>
</div>
)}
{(hasToolCalls && toolCallsHaveContents && (
<ToolCalls toolCalls={message.tool_calls} />
)) ||
(hasAnthropicToolCalls && (
<ToolCalls toolCalls={anthropicStreamedToolCalls} />
)) ||
(hasToolCalls && <ToolCalls toolCalls={message.tool_calls} />)}
{!hideToolCalls && (
<>
{(hasToolCalls && toolCallsHaveContents && (
<ToolCalls toolCalls={message.tool_calls} />
)) ||
(hasAnthropicToolCalls && (
<ToolCalls toolCalls={anthropicStreamedToolCalls} />
)) ||
(hasToolCalls && <ToolCalls toolCalls={message.tool_calls} />)}
</>
)}
<CustomComponent message={message} thread={thread} />
{isAgentInboxInterruptSchema(interrupt?.value) && isLastMessage && (
<ThreadView interrupt={interrupt.value[0]} />

View File

@@ -0,0 +1,29 @@
import * as React from "react";
import * as SwitchPrimitive from "@radix-ui/react-switch";
import { cn } from "@/lib/utils";
function Switch({
className,
...props
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
return (
<SwitchPrimitive.Root
data-slot="switch"
className={cn(
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 inline-flex h-5 w-9 shrink-0 items-center rounded-full border-2 border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
>
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
className={cn(
"bg-background pointer-events-none block size-4 rounded-full ring-0 shadow-lg transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
)}
/>
</SwitchPrimitive.Root>
);
}
export { Switch };