Merge pull request #39 from langchain-ai/dqbd/sdk-js-0.0.52

feat: upgrade to 0.0.52
This commit is contained in:
David Duong
2025-03-10 18:18:29 +01:00
committed by GitHub
8 changed files with 2230 additions and 4347 deletions

View File

@@ -106,17 +106,22 @@ export async function executor(
const fullWriteAccess = !!config.configurable?.permissions?.full_write_access; const fullWriteAccess = !!config.configurable?.permissions?.full_write_access;
const msg = ui.create("proposed-change", { ui.push(
{
name: "proposed-change",
content: {
toolCallId, toolCallId,
change: updateFileContents, change: updateFileContents,
planItem: nextPlanItem, planItem: nextPlanItem,
fullWriteAccess, fullWriteAccess,
}); },
msg.additional_kwargs["message_id"] = aiMessage.id; },
{ message: aiMessage },
);
return { return {
messages: [aiMessage], messages: [aiMessage],
ui: [msg], ui: ui.items,
timestamp: Date.now(), timestamp: Date.now(),
}; };
} }

View File

@@ -86,13 +86,18 @@ export async function planner(
], ],
}; };
const msg = ui.create("code-plan", { ui.push(
{
name: "code-plan",
content: {
toolCallId, toolCallId,
executedPlans, executedPlans,
rejectedPlans, rejectedPlans,
remainingPlans, remainingPlans,
}); },
msg.additional_kwargs["message_id"] = aiMessage.id; },
{ message: aiMessage },
);
const toolMessage: ToolMessage = { const toolMessage: ToolMessage = {
type: "tool", type: "tool",
@@ -103,7 +108,7 @@ export async function planner(
return { return {
messages: [aiMessage, toolMessage], messages: [aiMessage, toolMessage],
ui: [msg], ui: ui.items,
timestamp: Date.now(), timestamp: Date.now(),
}; };
} }

View File

@@ -176,29 +176,38 @@ export async function callTools(
if (stockbrokerToolCall) { if (stockbrokerToolCall) {
const prices = await getPricesForTicker(stockbrokerToolCall.args.ticker); const prices = await getPricesForTicker(stockbrokerToolCall.args.ticker);
ui.write("stock-price", { ui.push(
ticker: stockbrokerToolCall.args.ticker, {
...prices, name: "stock-price",
}); content: { ticker: stockbrokerToolCall.args.ticker, ...prices },
},
{ message },
);
} }
if (portfolioToolCall) { if (portfolioToolCall) {
ui.write("portfolio", {}); ui.push({ name: "portfolio", content: {} }, { message });
} }
if (buyStockToolCall) { if (buyStockToolCall) {
const snapshot = await getPriceSnapshotForTicker( const snapshot = await getPriceSnapshotForTicker(
buyStockToolCall.args.ticker, buyStockToolCall.args.ticker,
); );
ui.write("buy-stock", { ui.push(
{
name: "buy-stock",
content: {
toolCallId: toolCallId:
message.tool_calls?.find((tc) => tc.name === "buy-stock")?.id ?? "", message.tool_calls?.find((tc) => tc.name === "buy-stock")?.id ?? "",
snapshot, snapshot,
quantity: buyStockToolCall.args.quantity, quantity: buyStockToolCall.args.quantity,
}); },
},
{ message },
);
} }
return { return {
messages: [message], messages: [message],
ui: ui.collect as StockbrokerUpdate["ui"], ui: ui.items,
timestamp: Date.now(), timestamp: Date.now(),
}; };
} }

View File

@@ -87,32 +87,56 @@ export async function callTools(
} }
if (tripPlan.listAccommodations) { if (tripPlan.listAccommodations) {
ui.write("accommodations-list", { ui.push(
{
name: "accommodations-list",
content: {
toolCallId, toolCallId,
...getAccommodationsListProps(state.tripDetails), ...getAccommodationsListProps(state.tripDetails),
}); },
},
{ message: response },
);
} }
if (tripPlan.bookAccommodation && tripPlan.accommodationName) { if (tripPlan.bookAccommodation && tripPlan.accommodationName) {
ui.write("book-accommodation", { ui.push(
{
name: "book-accommodation",
content: {
tripDetails: state.tripDetails, tripDetails: state.tripDetails,
accommodationName: tripPlan.accommodationName, accommodationName: tripPlan.accommodationName,
}); },
},
{ message: response },
);
} }
if (tripPlan.listRestaurants) { if (tripPlan.listRestaurants) {
ui.write("restaurants-list", { tripDetails: state.tripDetails }); ui.push(
{
name: "restaurants-list",
content: { tripDetails: state.tripDetails },
},
{ message: response },
);
} }
if (tripPlan.bookRestaurant && tripPlan.restaurantName) { if (tripPlan.bookRestaurant && tripPlan.restaurantName) {
ui.write("book-restaurant", { ui.push(
{
name: "book-restaurant",
content: {
tripDetails: state.tripDetails, tripDetails: state.tripDetails,
restaurantName: tripPlan.restaurantName, restaurantName: tripPlan.restaurantName,
}); },
},
{ message: response },
);
} }
return { return {
messages: [response], messages: [response],
ui: ui.collect as TripPlannerUpdate["ui"], ui: ui.items,
timestamp: Date.now(), timestamp: Date.now(),
}; };
} }

View File

@@ -22,7 +22,7 @@
"@langchain/langgraph": "^0.2.49", "@langchain/langgraph": "^0.2.49",
"@langchain/langgraph-api": "^0.0.14", "@langchain/langgraph-api": "^0.0.14",
"@langchain/langgraph-cli": "^0.0.14", "@langchain/langgraph-cli": "^0.0.14",
"@langchain/langgraph-sdk": "^0.0.50", "@langchain/langgraph-sdk": "^0.0.52",
"@langchain/openai": "^0.4.4", "@langchain/openai": "^0.4.4",
"@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dialog": "^1.1.6",

6337
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,8 @@ import { MarkdownText } from "../markdown-text";
import { LoadExternalComponent } from "@langchain/langgraph-sdk/react-ui"; 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";
import { StringParam, useQueryParam } from "use-query-params";
import { MessageContentComplex } from "@langchain/core/messages"; import { MessageContentComplex } from "@langchain/core/messages";
import { Fragment } from "react/jsx-runtime";
function CustomComponent({ function CustomComponent({
message, message,
@@ -17,32 +17,28 @@ function CustomComponent({
message: Message; message: Message;
thread: ReturnType<typeof useStreamContext>; thread: ReturnType<typeof useStreamContext>;
}) { }) {
const [apiUrl] = useQueryParam("apiUrl", StringParam);
const meta = thread.getMessagesMetadata(message); const meta = thread.getMessagesMetadata(message);
const seenState = meta?.firstSeenState; const seenState = meta?.firstSeenState;
const customComponents = seenState?.values.ui const customComponents = seenState?.values.ui
?.slice() ?.slice()
.filter( .filter(({ additional_kwargs }) =>
({ additional_kwargs }) => !additional_kwargs.message_id
additional_kwargs.run_id === seenState.metadata?.run_id && ? additional_kwargs.run_id === seenState.metadata?.run_id
(!additional_kwargs.message_id || : additional_kwargs.message_id === message.id,
additional_kwargs.message_id === message.id),
); );
if (!customComponents?.length) return null; if (!customComponents?.length) return null;
return ( return (
<div key={message.id}> <Fragment key={message.id}>
{customComponents.map((customComponent) => ( {customComponents.map((customComponent) => (
<LoadExternalComponent <LoadExternalComponent
key={customComponent.id} key={customComponent.id}
apiUrl={apiUrl ?? undefined}
assistantId="agent"
stream={thread} stream={thread}
message={customComponent} message={customComponent}
meta={{ ui: customComponent }} meta={{ ui: customComponent }}
/> />
))} ))}
</div> </Fragment>
); );
} }

View File

@@ -1,9 +1,10 @@
import React, { createContext, useContext, ReactNode, useState } from "react"; import React, { createContext, useContext, ReactNode, useState } from "react";
import { useStream } from "@langchain/langgraph-sdk/react"; import { useStream } from "@langchain/langgraph-sdk/react";
import { type Message } from "@langchain/langgraph-sdk"; import { type Message } from "@langchain/langgraph-sdk";
import type { import {
UIMessage, uiMessageReducer,
RemoveUIMessage, type UIMessage,
type RemoveUIMessage,
} from "@langchain/langgraph-sdk/react-ui"; } 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";
@@ -24,7 +25,7 @@ const useTypedStream = useStream<
messages?: Message[] | Message | string; messages?: Message[] | Message | string;
ui?: (UIMessage | RemoveUIMessage)[] | UIMessage | RemoveUIMessage; ui?: (UIMessage | RemoveUIMessage)[] | UIMessage | RemoveUIMessage;
}; };
CustomUpdateType: UIMessage | RemoveUIMessage; CustomEventType: UIMessage | RemoveUIMessage;
} }
>; >;
@@ -53,6 +54,12 @@ const StreamSession = ({
apiKey: apiKey ?? undefined, apiKey: apiKey ?? undefined,
assistantId, assistantId,
threadId: threadId ?? null, threadId: threadId ?? null,
onCustomEvent: (event, options) => {
options.mutate((prev) => {
const ui = uiMessageReducer(prev.ui ?? [], event);
return { ...prev, ui };
});
},
onThreadId: (id) => { onThreadId: (id) => {
setThreadId(id); setThreadId(id);
// Refetch threads list when thread ID changes. // Refetch threads list when thread ID changes.