diff --git a/package.json b/package.json index e749ffc..15dc0c0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@langchain/langgraph-sdk": "*", "@langchain/openai": "^0.4.4", "@radix-ui/react-avatar": "^1.1.3", + "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", "@tailwindcss/postcss": "^4.0.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed11efa..59fa24e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,6 +43,9 @@ importers: '@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) + '@radix-ui/react-label': + specifier: ^2.1.2 + version: 2.1.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-slot': specifier: ^1.1.2 version: 1.1.2(@types/react@19.0.10)(react@19.0.0) @@ -854,6 +857,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-label@2.1.2': + resolution: {integrity: sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==} + 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-popover@1.1.6': resolution: {integrity: sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==} peerDependencies: @@ -3773,6 +3789,15 @@ snapshots: optionalDependencies: '@types/react': 19.0.10 + '@radix-ui/react-label@2.1.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)': + dependencies: + '@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) + 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-popover@1.1.6(@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 diff --git a/src/components/thread/index.tsx b/src/components/thread/index.tsx index f387f98..700124b 100644 --- a/src/components/thread/index.tsx +++ b/src/components/thread/index.tsx @@ -137,7 +137,9 @@ export function Thread() { onClick={() => setThreadId(null)} > - LangGraph Chat + + LangGraph Chat + -

LangGraph Chat

+

+ LangGraph Chat +

)} diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..4ef28a9 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,22 @@ +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; + +import { cn } from "@/lib/utils"; + +function Label({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { Label }; diff --git a/src/providers/Stream.tsx b/src/providers/Stream.tsx index d8f960e..a7a5173 100644 --- a/src/providers/Stream.tsx +++ b/src/providers/Stream.tsx @@ -6,6 +6,11 @@ import type { RemoveUIMessage, } from "@langchain/langgraph-sdk/react-ui/types"; import { useQueryParam, StringParam } from "use-query-params"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { LangGraphLogoSVG } from "@/components/icons/langgraph"; +import { Label } from "@/components/ui/label"; +import { ArrowRight } from "lucide-react"; const useTypedStream = useStream< { messages: Message[]; ui: UIMessage[] }, @@ -21,21 +26,23 @@ const useTypedStream = useStream< type StreamContextType = ReturnType; const StreamContext = createContext(undefined); -export const StreamProvider: React.FC<{ children: ReactNode }> = ({ +const StreamSession = ({ children, + apiUrl, + assistantId, +}: { + children: ReactNode; + apiUrl: string; + assistantId: string; }) => { const [threadId, setThreadId] = useQueryParam("threadId", StringParam); - const streamValue = useTypedStream({ - apiUrl: "http://localhost:2024", - assistantId: "agent", + apiUrl, + assistantId, threadId: threadId ?? null, onThreadId: setThreadId, }); - console.log("threadId", threadId); - console.log("streamValue", streamValue.values); - return ( {children} @@ -43,6 +50,98 @@ export const StreamProvider: React.FC<{ children: ReactNode }> = ({ ); }; +export const StreamProvider: React.FC<{ children: ReactNode }> = ({ + children, +}) => { + const [apiUrl, setApiUrl] = useQueryParam("apiUrl", StringParam); + const [assistantId, setAssistantId] = useQueryParam( + "assistantId", + StringParam, + ); + + if (!apiUrl || !assistantId) { + return ( +
+
+
+
+ +

+ LangGraph Chat +

+
+

+ Welcome to LangGraph Chat! Before you get started, you need to + enter the URL of the deployment and the assistant / graph ID. +

+
+
{ + e.preventDefault(); + + const form = e.target as HTMLFormElement; + const formData = new FormData(form); + const apiUrl = formData.get("apiUrl") as string; + const assistantId = formData.get("assistantId") as string; + + setApiUrl(apiUrl); + setAssistantId(assistantId); + console.log({ apiUrl, assistantId }); + + form.reset(); + }} + 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. +

+ setApiUrl(e.target.value)} + /> +
+ +
+ +

+ This is the ID of the graph (can be the graph name), or + assistant to fetch threads from, and invoke when actions are + taken. +

+ setAssistantId(e.target.value)} + /> +
+ +
+ +
+
+
+
+ ); + } + + return ( + + {children} + + ); +}; + // Create a custom hook to use the context export const useStreamContext = (): StreamContextType => { const context = useContext(StreamContext);