Merge pull request #15 from langchain-ai/dqbd/update-types

feat: update types, refine ui sdk
This commit is contained in:
David Duong
2025-03-05 21:28:40 +01:00
committed by GitHub
12 changed files with 66 additions and 56 deletions

View File

@@ -1,4 +1,4 @@
import { StockbrokerState } from "../types"; import { StockbrokerState, StockbrokerUpdate } from "../types";
import { ChatOpenAI } from "@langchain/openai"; import { ChatOpenAI } from "@langchain/openai";
import { typedUi } from "@langchain/langgraph-sdk/react-ui/server"; import { typedUi } from "@langchain/langgraph-sdk/react-ui/server";
import type ComponentMap from "../../uis/index"; import type ComponentMap from "../../uis/index";
@@ -32,7 +32,7 @@ const STOCKBROKER_TOOLS = [
export async function callTools( export async function callTools(
state: StockbrokerState, state: StockbrokerState,
config: LangGraphRunnableConfig, config: LangGraphRunnableConfig,
): Promise<Partial<StockbrokerState>> { ): Promise<StockbrokerUpdate> {
const ui = typedUi<typeof ComponentMap>(config); const ui = typedUi<typeof ComponentMap>(config);
const message = await llm.bindTools(STOCKBROKER_TOOLS).invoke([ const message = await llm.bindTools(STOCKBROKER_TOOLS).invoke([
@@ -65,8 +65,7 @@ export async function callTools(
return { return {
messages: [message], messages: [message],
// TODO: Fix the ui return type. ui: ui.collect as StockbrokerUpdate["ui"],
ui: ui.collect as any[],
timestamp: Date.now(), timestamp: Date.now(),
}; };
} }

View File

@@ -8,3 +8,4 @@ export const StockbrokerAnnotation = Annotation.Root({
}); });
export type StockbrokerState = typeof StockbrokerAnnotation.State; export type StockbrokerState = typeof StockbrokerAnnotation.State;
export type StockbrokerUpdate = typeof StockbrokerAnnotation.Update;

View File

@@ -1,11 +1,11 @@
import { ChatOpenAI } from "@langchain/openai"; import { ChatOpenAI } from "@langchain/openai";
import { TripPlannerState } from "../types"; import { TripPlannerState, TripPlannerUpdate } from "../types";
import { z } from "zod"; import { z } from "zod";
import { formatMessages } from "agent/utils/format-messages"; import { formatMessages } from "agent/utils/format-messages";
export async function classify( export async function classify(
state: TripPlannerState, state: TripPlannerState,
): Promise<Partial<TripPlannerState>> { ): Promise<TripPlannerUpdate> {
if (!state.tripDetails) { if (!state.tripDetails) {
// Can not classify if tripDetails are undefined // Can not classify if tripDetails are undefined
return {}; return {};

View File

@@ -1,5 +1,5 @@
import { ChatOpenAI } from "@langchain/openai"; import { ChatOpenAI } from "@langchain/openai";
import { TripDetails, TripPlannerState } from "../types"; import { TripDetails, TripPlannerState, TripPlannerUpdate } from "../types";
import { z } from "zod"; import { z } from "zod";
import { formatMessages } from "agent/utils/format-messages"; import { formatMessages } from "agent/utils/format-messages";
@@ -43,7 +43,7 @@ function calculateDates(
export async function extraction( export async function extraction(
state: TripPlannerState, state: TripPlannerState,
): Promise<Partial<TripPlannerState>> { ): Promise<TripPlannerUpdate> {
const schema = z.object({ const schema = z.object({
location: z location: z
.string() .string()

View File

@@ -1,4 +1,4 @@
import { TripPlannerState } from "../types"; import { TripPlannerState, TripPlannerUpdate } from "../types";
import { ChatOpenAI } from "@langchain/openai"; import { ChatOpenAI } from "@langchain/openai";
import { typedUi } from "@langchain/langgraph-sdk/react-ui/server"; import { typedUi } from "@langchain/langgraph-sdk/react-ui/server";
import type ComponentMap from "../../uis/index"; import type ComponentMap from "../../uis/index";
@@ -48,7 +48,7 @@ const schema = z.object({
export async function callTools( export async function callTools(
state: TripPlannerState, state: TripPlannerState,
config: LangGraphRunnableConfig, config: LangGraphRunnableConfig,
): Promise<Partial<TripPlannerState>> { ): Promise<TripPlannerUpdate> {
if (!state.tripDetails) { if (!state.tripDetails) {
throw new Error("No trip details found"); throw new Error("No trip details found");
} }
@@ -111,8 +111,7 @@ export async function callTools(
return { return {
messages: [response], messages: [response],
// TODO: Fix the ui return type. ui: ui.collect as TripPlannerUpdate["ui"],
ui: ui.collect as any[],
timestamp: Date.now(), timestamp: Date.now(),
}; };
} }

View File

@@ -16,3 +16,4 @@ export const TripPlannerAnnotation = Annotation.Root({
}); });
export type TripPlannerState = typeof TripPlannerAnnotation.State; export type TripPlannerState = typeof TripPlannerAnnotation.State;
export type TripPlannerUpdate = typeof TripPlannerAnnotation.Update;

View File

@@ -1,9 +1,16 @@
import { MessagesAnnotation, Annotation } from "@langchain/langgraph"; 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({ export const GenerativeUIAnnotation = Annotation.Root({
messages: MessagesAnnotation.spec["messages"], messages: MessagesAnnotation.spec["messages"],
ui: Annotation({ default: () => [], reducer: uiMessageReducer }), ui: Annotation<
UIMessage[],
UIMessage | RemoveUIMessage | (UIMessage | RemoveUIMessage)[]
>({ default: () => [], reducer: uiMessageReducer }),
timestamp: Annotation<number>, timestamp: Annotation<number>,
next: Annotation<"stockbroker" | "tripPlanner" | "generalInput">(), next: Annotation<"stockbroker" | "tripPlanner" | "generalInput">(),
}); });

View File

@@ -1,12 +1,20 @@
import "./index.css"; import "./index.css";
import { useStream } from "@langchain/langgraph-sdk/react"; import {
import type { AIMessage, Message } from "@langchain/langgraph-sdk"; useStreamContext,
type UIMessage,
} from "@langchain/langgraph-sdk/react-ui";
import type { AIMessage, Message, ToolMessage } from "@langchain/langgraph-sdk";
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
export default function StockPrice(props: { export default function StockPrice(props: {
instruction: string; instruction: string;
logo: string; logo: string;
}) { }) {
const thread = useStreamContext<
{ messages: Message[]; ui: UIMessage[] },
{ MetaType: { ui: UIMessage | undefined } }
>();
const [quantity, setQuantity] = useState(1); const [quantity, setQuantity] = useState(1);
const [stockData, setStockData] = useState({ const [stockData, setStockData] = useState({
symbol: "AAPL", symbol: "AAPL",
@@ -51,23 +59,16 @@ export default function StockPrice(props: {
const [limitPrice, setLimitPrice] = useState(stockData.price.toFixed(2)); const [limitPrice, setLimitPrice] = useState(stockData.price.toFixed(2));
const [showOrderSuccess, setShowOrderSuccess] = useState(false); const [showOrderSuccess, setShowOrderSuccess] = useState(false);
// useStream should be able to be infered from context const aiTool = thread.messages.findLast(
const thread = useStream<{ messages: Message[] }>({ (message): message is AIMessage =>
assistantId: "assistant_123", message.type === "ai" && !!message.tool_calls?.length,
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 toolCallId = aiTool?.tool_calls?.[0]?.id; 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 // Simulated price history generation on component mount
useEffect(() => { useEffect(() => {
@@ -248,6 +249,7 @@ export default function StockPrice(props: {
const range = max - min || 1; const range = max - min || 1;
const chartPath = generateChartPath(); const chartPath = generateChartPath();
if (toolResponse) return <div>Responded</div>;
return ( return (
<div className="w-full max-w-md bg-white rounded-xl shadow-lg overflow-hidden border border-gray-200"> <div className="w-full max-w-md bg-white rounded-xl shadow-lg overflow-hidden border border-gray-200">
<div <div

View File

@@ -58,9 +58,9 @@
"zod": "^3.24.2" "zod": "^3.24.2"
}, },
"resolutions": { "resolutions": {
"@langchain/langgraph-api": "next", "@langchain/langgraph-api": "0.0.14-experimental.1",
"@langchain/langgraph-cli": "next", "@langchain/langgraph-cli": "0.0.14-experimental.1",
"@langchain/langgraph-sdk": "next" "@langchain/langgraph-sdk": "0.0.47-experimental.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.19.0", "@eslint/js": "^9.19.0",

40
pnpm-lock.yaml generated
View File

@@ -5,9 +5,9 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
overrides: overrides:
"@langchain/langgraph-api": next "@langchain/langgraph-api": 0.0.14-experimental.1
"@langchain/langgraph-cli": next "@langchain/langgraph-cli": 0.0.14-experimental.1
"@langchain/langgraph-sdk": next "@langchain/langgraph-sdk": 0.0.47-experimental.0
importers: importers:
.: .:
@@ -34,14 +34,14 @@ importers:
specifier: ^0.2.49 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) version: 0.2.49(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
"@langchain/langgraph-api": "@langchain/langgraph-api":
specifier: next specifier: 0.0.14-experimental.1
version: 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) version: 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)
"@langchain/langgraph-cli": "@langchain/langgraph-cli":
specifier: next specifier: 0.0.14-experimental.1
version: 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) version: 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)
"@langchain/langgraph-sdk": "@langchain/langgraph-sdk":
specifier: next specifier: 0.0.47-experimental.0
version: 0.0.46-experimental.0(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0) version: 0.0.47-experimental.0(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))(react@19.0.0)
"@langchain/openai": "@langchain/openai":
specifier: ^0.4.4 specifier: ^0.4.4
version: 0.4.4(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2))) version: 0.4.4(@langchain/core@0.3.41(openai@4.85.4(zod@3.24.2)))
@@ -1087,10 +1087,10 @@ packages:
peerDependencies: peerDependencies:
"@langchain/core": ">=0.3.17 <0.4.0" "@langchain/core": ">=0.3.17 <0.4.0"
"@langchain/langgraph-api@0.0.14-experimental.0": "@langchain/langgraph-api@0.0.14-experimental.1":
resolution: resolution:
{ {
integrity: sha512-Cc72BHOXS0DhDvlkF3eW6V9CxvMRm0BI2J+ftpZUDG4s3nJjnp4p5F/fXkCnu9hks7w9IUrp4CGXzK4LUEf2iw==, integrity: sha512-gSQzZZk9tIrxXMQjudQbYHXPeK7l3Y/YbzCtnH6hWHvETQOZApUn0G18O5hWT9iYaAzZfSS8ExG7y6YM0MsFTQ==,
} }
engines: { node: ^18.19.0 || >=20.16.0 } engines: { node: ^18.19.0 || >=20.16.0 }
peerDependencies: peerDependencies:
@@ -1108,18 +1108,18 @@ packages:
peerDependencies: peerDependencies:
"@langchain/core": ">=0.2.31 <0.4.0" "@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: resolution:
{ {
integrity: sha512-31rdexCp4tR5849Qe3lstVai6VDm5+CFniGSQT08dghlITm4G0g35/jak9dFksavwLJ1NxeMTLuNJUfB5MocyA==, integrity: sha512-S8Y7WrBPsNZR7wUyWj3De0sEdTTf+ipJf1lCrJho+moL9TVXUXUE+oFoMb1G/uHvt8Q/FCSE9BfadEg4JUb5MQ==,
} }
engines: { node: ^18.19.0 || >=20.16.0 } engines: { node: ^18.19.0 || >=20.16.0 }
hasBin: true hasBin: true
"@langchain/langgraph-sdk@0.0.46-experimental.0": "@langchain/langgraph-sdk@0.0.47-experimental.0":
resolution: resolution:
{ {
integrity: sha512-ADzrxf/Rx8I2fe2lcXgIsB/3sG0uTLsG8S+HIA749yveWo1SQrlcy9+Smkla16y9PWdQpKeztrhNGI5g0BKxDw==, integrity: sha512-di60Pi2knQbe/sjOB3gNbNQNuTIhj0Yjls0SfEYeWDHirSN9heumPB/oxvwaxLBA8JKhuHg2h5lKUxAIT4b+aA==,
} }
peerDependencies: peerDependencies:
"@langchain/core": ">=0.2.31 <0.4.0" "@langchain/core": ">=0.2.31 <0.4.0"
@@ -5996,7 +5996,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- zod - 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: dependencies:
"@babel/code-frame": 7.26.2 "@babel/code-frame": 7.26.2
"@hono/node-server": 1.13.8(hono@4.7.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)) "@langchain/core": 0.3.41(openai@4.85.4(zod@3.24.2))
uuid: 10.0.0 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: dependencies:
"@babel/code-frame": 7.26.2 "@babel/code-frame": 7.26.2
"@commander-js/extra-typings": 13.1.0(commander@13.1.0) "@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 chokidar: 4.0.3
commander: 13.1.0 commander: 13.1.0
dedent: 1.5.3 dedent: 1.5.3
@@ -6061,7 +6061,7 @@ snapshots:
- supports-color - supports-color
- typescript - 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: dependencies:
"@types/json-schema": 7.0.15 "@types/json-schema": 7.0.15
p-queue: 6.6.2 p-queue: 6.6.2
@@ -6075,7 +6075,7 @@ snapshots:
dependencies: dependencies:
"@langchain/core": 0.3.41(openai@4.85.4(zod@3.24.2)) "@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-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 uuid: 10.0.0
zod: 3.24.2 zod: 3.24.2
transitivePeerDependencies: transitivePeerDependencies:

View File

@@ -4,7 +4,7 @@ import { getContentString } from "../utils";
import { BranchSwitcher, CommandBar } from "./shared"; import { BranchSwitcher, CommandBar } from "./shared";
import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { MarkdownText } from "../markdown-text"; 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 { cn } from "@/lib/utils";
import { ToolCalls, ToolResult } from "./tool-calls"; import { ToolCalls, ToolResult } from "./tool-calls";
@@ -36,6 +36,7 @@ function CustomComponent({
assistantId="agent" assistantId="agent"
stream={thread} stream={thread}
message={customComponent} message={customComponent}
meta={{ ui: customComponent }}
/> />
)} )}
</div> </div>

View File

@@ -4,7 +4,7 @@ import { type Message } from "@langchain/langgraph-sdk";
import type { import type {
UIMessage, UIMessage,
RemoveUIMessage, RemoveUIMessage,
} from "@langchain/langgraph-sdk/react-ui/types"; } from "@langchain/langgraph-sdk/react-ui";
import { useQueryParam, StringParam } from "use-query-params"; import { useQueryParam, StringParam } from "use-query-params";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";