diff --git a/agent/stockbroker/nodes/tools.tsx b/agent/stockbroker/nodes/tools.tsx index 9fbe432..d434b06 100644 --- a/agent/stockbroker/nodes/tools.tsx +++ b/agent/stockbroker/nodes/tools.tsx @@ -1,4 +1,4 @@ -import { StockbrokerState } from "../types"; +import { StockbrokerState, StockbrokerUpdate } from "../types"; import { ChatOpenAI } from "@langchain/openai"; import { typedUi } from "@langchain/langgraph-sdk/react-ui/server"; import type ComponentMap from "../../uis/index"; @@ -32,7 +32,7 @@ const STOCKBROKER_TOOLS = [ export async function callTools( state: StockbrokerState, config: LangGraphRunnableConfig, -): Promise> { +): Promise { const ui = typedUi(config); const message = await llm.bindTools(STOCKBROKER_TOOLS).invoke([ @@ -65,8 +65,7 @@ export async function callTools( return { messages: [message], - // TODO: Fix the ui return type. - ui: ui.collect as any[], + ui: ui.collect as StockbrokerUpdate["ui"], timestamp: Date.now(), }; } diff --git a/agent/stockbroker/types.ts b/agent/stockbroker/types.ts index ebe3fdb..ab8f43b 100644 --- a/agent/stockbroker/types.ts +++ b/agent/stockbroker/types.ts @@ -8,3 +8,4 @@ export const StockbrokerAnnotation = Annotation.Root({ }); export type StockbrokerState = typeof StockbrokerAnnotation.State; +export type StockbrokerUpdate = typeof StockbrokerAnnotation.Update; diff --git a/agent/trip-planner/nodes/classify.ts b/agent/trip-planner/nodes/classify.ts index 945c056..05e2df8 100644 --- a/agent/trip-planner/nodes/classify.ts +++ b/agent/trip-planner/nodes/classify.ts @@ -1,11 +1,11 @@ import { ChatOpenAI } from "@langchain/openai"; -import { TripPlannerState } from "../types"; +import { TripPlannerState, TripPlannerUpdate } from "../types"; import { z } from "zod"; import { formatMessages } from "agent/utils/format-messages"; export async function classify( state: TripPlannerState, -): Promise> { +): Promise { if (!state.tripDetails) { // Can not classify if tripDetails are undefined return {}; diff --git a/agent/trip-planner/nodes/extraction.tsx b/agent/trip-planner/nodes/extraction.tsx index aee5a95..af68a8f 100644 --- a/agent/trip-planner/nodes/extraction.tsx +++ b/agent/trip-planner/nodes/extraction.tsx @@ -1,5 +1,5 @@ import { ChatOpenAI } from "@langchain/openai"; -import { TripDetails, TripPlannerState } from "../types"; +import { TripDetails, TripPlannerState, TripPlannerUpdate } from "../types"; import { z } from "zod"; import { formatMessages } from "agent/utils/format-messages"; @@ -43,7 +43,7 @@ function calculateDates( export async function extraction( state: TripPlannerState, -): Promise> { +): Promise { const schema = z.object({ location: z .string() diff --git a/agent/trip-planner/nodes/tools.tsx b/agent/trip-planner/nodes/tools.tsx index 178b8c3..d906148 100644 --- a/agent/trip-planner/nodes/tools.tsx +++ b/agent/trip-planner/nodes/tools.tsx @@ -1,4 +1,4 @@ -import { TripPlannerState } from "../types"; +import { TripPlannerState, TripPlannerUpdate } from "../types"; import { ChatOpenAI } from "@langchain/openai"; import { typedUi } from "@langchain/langgraph-sdk/react-ui/server"; import type ComponentMap from "../../uis/index"; @@ -49,7 +49,7 @@ const schema = z.object({ export async function callTools( state: TripPlannerState, config: LangGraphRunnableConfig, -): Promise> { +): Promise { if (!state.tripDetails) { throw new Error("No trip details found"); } @@ -111,8 +111,7 @@ export async function callTools( return { messages: [response], - // TODO: Fix the ui return type. - ui: ui.collect as any[], + ui: ui.collect as TripPlannerUpdate["ui"], timestamp: Date.now(), }; } diff --git a/agent/trip-planner/types.ts b/agent/trip-planner/types.ts index 9e546bb..391aa0d 100644 --- a/agent/trip-planner/types.ts +++ b/agent/trip-planner/types.ts @@ -16,3 +16,4 @@ export const TripPlannerAnnotation = Annotation.Root({ }); export type TripPlannerState = typeof TripPlannerAnnotation.State; +export type TripPlannerUpdate = typeof TripPlannerAnnotation.Update; diff --git a/agent/types.ts b/agent/types.ts index 1962a6b..8894f88 100644 --- a/agent/types.ts +++ b/agent/types.ts @@ -1,9 +1,16 @@ import { MessagesAnnotation, Annotation } from "@langchain/langgraph"; -import { uiMessageReducer } from "@langchain/langgraph-sdk/react-ui/types"; +import { + RemoveUIMessage, + UIMessage, + uiMessageReducer, +} from "@langchain/langgraph-sdk/react-ui/server"; export const GenerativeUIAnnotation = Annotation.Root({ messages: MessagesAnnotation.spec["messages"], - ui: Annotation({ default: () => [], reducer: uiMessageReducer }), + ui: Annotation< + UIMessage[], + UIMessage | RemoveUIMessage | (UIMessage | RemoveUIMessage)[] + >({ default: () => [], reducer: uiMessageReducer }), timestamp: Annotation, next: Annotation<"stockbroker" | "tripPlanner" | "generalInput">(), }); diff --git a/agent/uis/stockbroker/stock-price/index.tsx b/agent/uis/stockbroker/stock-price/index.tsx index f279781..8413a2c 100644 --- a/agent/uis/stockbroker/stock-price/index.tsx +++ b/agent/uis/stockbroker/stock-price/index.tsx @@ -1,12 +1,20 @@ import "./index.css"; -import { useStream } from "@langchain/langgraph-sdk/react"; -import type { AIMessage, Message } from "@langchain/langgraph-sdk"; +import { + useStreamContext, + type UIMessage, +} from "@langchain/langgraph-sdk/react-ui"; +import type { AIMessage, Message, ToolMessage } from "@langchain/langgraph-sdk"; import { useState, useEffect, useCallback } from "react"; export default function StockPrice(props: { instruction: string; logo: string; }) { + const thread = useStreamContext< + { messages: Message[]; ui: UIMessage[] }, + { MetaType: { ui: UIMessage | undefined } } + >(); + const [quantity, setQuantity] = useState(1); const [stockData, setStockData] = useState({ symbol: "AAPL", @@ -51,23 +59,16 @@ export default function StockPrice(props: { const [limitPrice, setLimitPrice] = useState(stockData.price.toFixed(2)); const [showOrderSuccess, setShowOrderSuccess] = useState(false); - // useStream should be able to be infered from context - const thread = useStream<{ messages: Message[] }>({ - assistantId: "assistant_123", - apiUrl: "http://localhost:3123", - }); - - const messagesCopy = thread.messages; - - const aiTool = messagesCopy - .slice() - .reverse() - .find( - (message): message is AIMessage => - message.type === "ai" && !!message.tool_calls?.length, - ); + const aiTool = thread.messages.findLast( + (message): message is AIMessage => + message.type === "ai" && !!message.tool_calls?.length, + ); const toolCallId = aiTool?.tool_calls?.[0]?.id; + const toolResponse = thread.messages.findLast( + (message): message is ToolMessage => + message.type === "tool" && message.tool_call_id === toolCallId, + ); // Simulated price history generation on component mount useEffect(() => { @@ -248,6 +249,7 @@ export default function StockPrice(props: { const range = max - min || 1; const chartPath = generateChartPath(); + if (toolResponse) return
Responded
; return (
=0.3.17 <0.4.0" - "@langchain/langgraph-api@0.0.14-experimental.0": + "@langchain/langgraph-api@0.0.14-experimental.1": resolution: { - integrity: sha512-Cc72BHOXS0DhDvlkF3eW6V9CxvMRm0BI2J+ftpZUDG4s3nJjnp4p5F/fXkCnu9hks7w9IUrp4CGXzK4LUEf2iw==, + integrity: sha512-gSQzZZk9tIrxXMQjudQbYHXPeK7l3Y/YbzCtnH6hWHvETQOZApUn0G18O5hWT9iYaAzZfSS8ExG7y6YM0MsFTQ==, } engines: { node: ^18.19.0 || >=20.16.0 } peerDependencies: @@ -1108,18 +1108,18 @@ packages: peerDependencies: "@langchain/core": ">=0.2.31 <0.4.0" - "@langchain/langgraph-cli@0.0.14-experimental.0": + "@langchain/langgraph-cli@0.0.14-experimental.1": resolution: { - integrity: sha512-31rdexCp4tR5849Qe3lstVai6VDm5+CFniGSQT08dghlITm4G0g35/jak9dFksavwLJ1NxeMTLuNJUfB5MocyA==, + integrity: sha512-S8Y7WrBPsNZR7wUyWj3De0sEdTTf+ipJf1lCrJho+moL9TVXUXUE+oFoMb1G/uHvt8Q/FCSE9BfadEg4JUb5MQ==, } engines: { node: ^18.19.0 || >=20.16.0 } hasBin: true - "@langchain/langgraph-sdk@0.0.46-experimental.0": + "@langchain/langgraph-sdk@0.0.47-experimental.0": resolution: { - integrity: sha512-ADzrxf/Rx8I2fe2lcXgIsB/3sG0uTLsG8S+HIA749yveWo1SQrlcy9+Smkla16y9PWdQpKeztrhNGI5g0BKxDw==, + integrity: sha512-di60Pi2knQbe/sjOB3gNbNQNuTIhj0Yjls0SfEYeWDHirSN9heumPB/oxvwaxLBA8JKhuHg2h5lKUxAIT4b+aA==, } peerDependencies: "@langchain/core": ">=0.2.31 <0.4.0" @@ -5996,7 +5996,7 @@ snapshots: transitivePeerDependencies: - zod - "@langchain/langgraph-api@0.0.14-experimental.0(@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.14-experimental.1(@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)": dependencies: "@babel/code-frame": 7.26.2 "@hono/node-server": 1.13.8(hono@4.7.2) @@ -6032,11 +6032,11 @@ snapshots: "@langchain/core": 0.3.41(openai@4.85.4(zod@3.24.2)) uuid: 10.0.0 - "@langchain/langgraph-cli@0.0.14-experimental.0(@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.14-experimental.1(@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)": dependencies: "@babel/code-frame": 7.26.2 "@commander-js/extra-typings": 13.1.0(commander@13.1.0) - "@langchain/langgraph-api": 0.0.14-experimental.0(@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.14-experimental.1(@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) chokidar: 4.0.3 commander: 13.1.0 dedent: 1.5.3 @@ -6061,7 +6061,7 @@ snapshots: - supports-color - typescript - "@langchain/langgraph-sdk@0.0.46-experimental.0(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)": + "@langchain/langgraph-sdk@0.0.47-experimental.0(@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 @@ -6075,7 +6075,7 @@ snapshots: 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.46-experimental.0(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0) + "@langchain/langgraph-sdk": 0.0.47-experimental.0(@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: diff --git a/src/components/thread/messages/ai.tsx b/src/components/thread/messages/ai.tsx index 91fa365..57c441c 100644 --- a/src/components/thread/messages/ai.tsx +++ b/src/components/thread/messages/ai.tsx @@ -4,7 +4,7 @@ import { getContentString } from "../utils"; import { BranchSwitcher, CommandBar } from "./shared"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { MarkdownText } from "../markdown-text"; -import { LoadExternalComponent } from "@langchain/langgraph-sdk/react-ui/client"; +import { LoadExternalComponent } from "@langchain/langgraph-sdk/react-ui"; import { cn } from "@/lib/utils"; import { ToolCalls, ToolResult } from "./tool-calls"; @@ -36,6 +36,7 @@ function CustomComponent({ assistantId="agent" stream={thread} message={customComponent} + meta={{ ui: customComponent }} /> )}
diff --git a/src/providers/Stream.tsx b/src/providers/Stream.tsx index df45fb4..baaa00f 100644 --- a/src/providers/Stream.tsx +++ b/src/providers/Stream.tsx @@ -4,7 +4,7 @@ import { type Message } from "@langchain/langgraph-sdk"; import type { UIMessage, RemoveUIMessage, -} from "@langchain/langgraph-sdk/react-ui/types"; +} from "@langchain/langgraph-sdk/react-ui"; import { useQueryParam, StringParam } from "use-query-params"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; @@ -110,7 +110,9 @@ export const StreamProvider: React.FC<{ children: ReactNode }> = ({ className="flex flex-col gap-6 p-6 bg-muted/50" >
- +

This is the URL of your LangGraph deployment. Can be a local, or production deployment. @@ -120,11 +122,14 @@ export const StreamProvider: React.FC<{ children: ReactNode }> = ({ name="apiUrl" className="bg-background" defaultValue={apiUrl ?? "http://localhost:2024"} + required />

- +

This is the ID of the graph (can be the graph name), or assistant to fetch threads from, and invoke when actions are @@ -135,14 +140,17 @@ export const StreamProvider: React.FC<{ children: ReactNode }> = ({ name="assistantId" className="bg-background" defaultValue={assistantId ?? "agent"} + required />

- This value is stored in your browser's local storage and is only - used to authenticate requests sent to your LangGraph server. + This is NOT required if using a local LangGraph + server. This value is stored in your browser's local storage and + is only used to authenticate requests sent to your LangGraph + server.