fix(sdk): send metadata when creating a new thread with useStream

This commit is contained in:
Tat Dat Duong
2025-07-09 14:33:18 +02:00
parent ec61207573
commit 2f26f2fa66
5 changed files with 89 additions and 1 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"@langchain/langgraph-api": patch
"@langchain/langgraph-cli": patch
"@langchain/langgraph-ui": patch
---
Expose get/delete thread endpoint to embed server
+5
View File
@@ -0,0 +1,5 @@
---
"@langchain/langgraph-sdk": patch
---
Send metadata when creating a new thread
@@ -111,6 +111,27 @@ export function createEmbedServer(options: {
return jsonExtra(c, { thread_id: threadId });
});
api.get(
"/threads/:thread_id",
zValidator("param", z.object({ thread_id: z.string().uuid() })),
async (c) => {
// Get Thread
const { thread_id } = c.req.valid("param");
const thread = await options.threads.get(thread_id);
return jsonExtra(c, thread);
}
);
api.delete(
"/threads/:thread_id",
zValidator("param", z.object({ thread_id: z.string().uuid() })),
async (c) => {
const { thread_id } = c.req.valid("param");
await options.threads.delete(thread_id);
return new Response(null, { status: 204 });
}
);
api.get(
"/threads/:thread_id/state",
zValidator("param", z.object({ thread_id: z.string().uuid() })),
+55 -1
View File
@@ -6,7 +6,7 @@ import { userEvent } from "@testing-library/user-event";
import { setupServer } from "msw/node";
import { http } from "msw";
import { useStream } from "@langchain/langgraph-sdk/react";
import type { Message } from "@langchain/langgraph-sdk";
import { Client, type Message } from "@langchain/langgraph-sdk";
import { StateGraph, MessagesAnnotation, START } from "@langchain/langgraph";
import { MemorySaver } from "@langchain/langgraph-checkpoint";
@@ -444,4 +444,58 @@ describe("useStream", () => {
expect(onStopCallback).not.toHaveBeenCalled();
});
});
it("make sure to pass metadata to the thread", async () => {
const user = userEvent.setup();
const onStopCallback = vi.fn();
const threadId = randomUUID();
function TestComponent() {
const { submit, messages } = useStream({
assistantId: "agent",
apiKey: "test-api-key",
onStop: onStopCallback,
});
return (
<div>
<div data-testid="messages">
{messages.map((msg, i) => (
<div key={msg.id ?? i} data-testid={`message-${i}`}>
{typeof msg.content === "string"
? msg.content
: JSON.stringify(msg.content)}
</div>
))}
</div>
<button
data-testid="submit"
onClick={() =>
submit(
{ messages: [{ content: "Hello", type: "human" }] },
{ metadata: { random: "123" }, threadId }
)
}
>
Send
</button>
</div>
);
}
render(<TestComponent />);
await user.click(screen.getByTestId("submit"));
await waitFor(() => {
expect(screen.getByTestId("message-0")).toHaveTextContent("Hello");
expect(screen.getByTestId("message-1")).toHaveTextContent("Hey");
});
const client = new Client();
const thread = await client.threads.get(threadId);
expect(thread.metadata).toMatchObject({ random: "123" });
});
});
+1
View File
@@ -1119,6 +1119,7 @@ export function useStream<
if (!usableThreadId) {
const thread = await client.threads.create({
threadId: submitOptions?.threadId,
metadata: submitOptions?.metadata,
});
onThreadId(thread.thread_id);
usableThreadId = thread.thread_id;