├── stockbroker ├── frontend │ ├── .eslintrc.json │ ├── public │ │ ├── logo.jpeg │ │ ├── vercel.svg │ │ └── next.svg │ ├── next.config.mjs │ ├── src │ │ ├── app │ │ │ ├── favicon.ico │ │ │ ├── page.tsx │ │ │ ├── layout.tsx │ │ │ ├── api │ │ │ │ └── [..._path] │ │ │ │ │ └── route.ts │ │ │ └── globals.css │ │ ├── constants.ts │ │ ├── utils │ │ │ ├── shadcnUtils.ts │ │ │ ├── cookies.ts │ │ │ ├── chatApi.ts │ │ │ └── streamHandler.ts │ │ ├── types.ts │ │ └── components │ │ │ ├── SkeletonMessage.tsx │ │ │ ├── MessageList.tsx │ │ │ ├── HomeComponent.tsx │ │ │ ├── Message.tsx │ │ │ ├── Alert.tsx │ │ │ ├── InputArea.tsx │ │ │ ├── Settings.tsx │ │ │ ├── Interrupted.tsx │ │ │ ├── APIDocsAlert.tsx │ │ │ ├── ToolCall.tsx │ │ │ └── ChatInterface.tsx │ ├── .env.example │ ├── postcss.config.mjs │ ├── vercel.json │ ├── components.json │ ├── .prettierrc │ ├── .gitignore │ ├── tsconfig.json │ ├── package.json │ └── tailwind.config.ts ├── backend │ ├── .gitignore │ ├── langgraph.json │ ├── .env.example │ ├── tsconfig.json │ ├── package.json │ └── src │ │ ├── types.ts │ │ ├── index.ts │ │ └── tools.ts ├── turbo.json ├── package.json └── README.md ├── streaming_messages_frontend ├── .eslintrc.json ├── src │ ├── constants.ts │ ├── app │ │ ├── favicon.ico │ │ ├── page.tsx │ │ ├── layout.tsx │ │ ├── globals.css │ │ └── api │ │ │ └── [..._path] │ │ │ └── route.ts │ ├── utils │ │ ├── cookies.ts │ │ ├── chatApi.ts │ │ └── streamHandler.ts │ ├── types.ts │ └── components │ │ ├── SkeletonMessage.tsx │ │ ├── MessageList.tsx │ │ ├── HomeComponent.tsx │ │ ├── InputArea.tsx │ │ ├── Message.tsx │ │ ├── Interrupted.tsx │ │ ├── Settings.tsx │ │ ├── ToolCall.tsx │ │ └── ChatInterface.tsx ├── next.config.mjs ├── public │ ├── logo.jpeg │ ├── vercel.svg │ └── next.svg ├── .env.example ├── postcss.config.mjs ├── .prettierrc ├── .gitignore ├── tailwind.config.ts ├── tsconfig.json ├── README.md └── package.json ├── intro ├── .gitignore ├── langgraph.json ├── .env.example ├── tsconfig.json ├── README.md ├── package.json └── src │ └── index.ts ├── human_in_the_loop ├── .gitignore ├── langgraph.json ├── .env.example ├── tsconfig.json ├── src │ ├── utils.ts │ ├── dynamic_breakpoints.ts │ └── human_in_the_loop.ts ├── package.json └── README.md ├── streaming_messages ├── .gitignore ├── langgraph.json ├── .env.example ├── src │ ├── runnable │ │ ├── stream_messages.ts │ │ ├── stream_values.ts │ │ ├── stream_updates.ts │ │ ├── stream_events.ts │ │ └── graph.ts │ ├── langgraph_sdk │ │ ├── stream_values.ts │ │ ├── stream_updates.ts │ │ ├── stream_events.ts │ │ └── stream_messages.ts │ └── utils.ts ├── tsconfig.json ├── package.json └── README.md ├── README.md └── .gitignore /stockbroker/frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /streaming_messages_frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /streaming_messages_frontend/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const ASSISTANT_ID_COOKIE = "ls_assistant_id"; 2 | -------------------------------------------------------------------------------- /intro/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist 3 | .turbo 4 | .env 5 | pdfs/** 6 | src/generated/** 7 | .langgraph_api -------------------------------------------------------------------------------- /stockbroker/frontend/public/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bracesproul/langgraphjs-examples/HEAD/stockbroker/frontend/public/logo.jpeg -------------------------------------------------------------------------------- /human_in_the_loop/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist 3 | .turbo 4 | .env 5 | pdfs/** 6 | src/generated/** 7 | # LangGraph API 8 | .langgraph_api 9 | -------------------------------------------------------------------------------- /stockbroker/frontend/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bracesproul/langgraphjs-examples/HEAD/stockbroker/frontend/src/app/favicon.ico -------------------------------------------------------------------------------- /streaming_messages/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist 3 | .turbo 4 | .env 5 | pdfs/** 6 | src/generated/** 7 | # LangGraph API 8 | .langgraph_api 9 | -------------------------------------------------------------------------------- /stockbroker/backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist 3 | .turbo 4 | .env 5 | pdfs/** 6 | src/generated/** 7 | # LangGraph API 8 | .langgraph_api 9 | -------------------------------------------------------------------------------- /streaming_messages_frontend/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const ASSISTANT_ID_COOKIE = "ls_assistant_id"; 2 | export const SEEN_API_TOAST_COOKIE = "ls_seen_api_toast"; 3 | -------------------------------------------------------------------------------- /streaming_messages_frontend/public/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bracesproul/langgraphjs-examples/HEAD/streaming_messages_frontend/public/logo.jpeg -------------------------------------------------------------------------------- /streaming_messages_frontend/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bracesproul/langgraphjs-examples/HEAD/streaming_messages_frontend/src/app/favicon.ico -------------------------------------------------------------------------------- /streaming_messages_frontend/.env.example: -------------------------------------------------------------------------------- 1 | LANGGRAPH_API_URL=http://localhost:8123 # Or your production URL 2 | LANGCHAIN_API_KEY=YOUR_API_KEY 3 | NEXT_PUBLIC_LANGGRAPH_GRAPH_ID=YOUR_GRAPH_ID -------------------------------------------------------------------------------- /stockbroker/frontend/.env.example: -------------------------------------------------------------------------------- 1 | LANGGRAPH_API_URL=http://localhost:8123 2 | NEXT_PUBLIC_API_URL=http://localhost:3000/api 3 | LANGCHAIN_API_KEY=YOUR_API_KEY 4 | NEXT_PUBLIC_LANGGRAPH_GRAPH_ID=YOUR_GRAPH_ID -------------------------------------------------------------------------------- /stockbroker/frontend/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /intro/langgraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "node_version": "20", 3 | "dockerfile_lines": [], 4 | "dependencies": ["."], 5 | "graphs": { 6 | "simple_agent": "./src/index.ts:graph" 7 | }, 8 | "env": "../.env" 9 | } -------------------------------------------------------------------------------- /streaming_messages_frontend/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /stockbroker/backend/langgraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "node_version": "20", 3 | "dockerfile_lines": [], 4 | "dependencies": ["."], 5 | "graphs": { 6 | "stockbroker": "./src/index.ts:graph" 7 | }, 8 | "env": ".env" 9 | } -------------------------------------------------------------------------------- /stockbroker/frontend/src/utils/shadcnUtils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /stockbroker/frontend/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "buildCommand": "yarn build", 3 | "trailingSlash": false, 4 | "redirects": [ 5 | { 6 | "source": "/openapi.json", 7 | "destination": "/api/openapi.json" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /human_in_the_loop/langgraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "node_version": "20", 3 | "dockerfile_lines": [], 4 | "dependencies": ["."], 5 | "graphs": { 6 | "human_in_the_loop": "./src/human_in_the_loop.ts:graph" 7 | }, 8 | "env": ".env" 9 | } 10 | 11 | -------------------------------------------------------------------------------- /streaming_messages/langgraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "node_version": "20", 3 | "dockerfile_lines": [], 4 | "dependencies": ["."], 5 | "graphs": { 6 | "streaming_messages": "./src/runnable/graph.ts:graph" 7 | }, 8 | "env": ".env" 9 | } 10 | 11 | -------------------------------------------------------------------------------- /human_in_the_loop/.env.example: -------------------------------------------------------------------------------- 1 | # ------------------LangSmith tracing------------------ 2 | LANGCHAIN_API_KEY= 3 | LANGCHAIN_TRACING_V2=true 4 | LANGCHAIN_CALLBACKS_BACKGROUND=true 5 | # ----------------------------------------------------- 6 | 7 | OPENAI_API_KEY= 8 | -------------------------------------------------------------------------------- /streaming_messages/.env.example: -------------------------------------------------------------------------------- 1 | # ------------------LangSmith tracing------------------ 2 | LANGCHAIN_API_KEY= 3 | LANGCHAIN_TRACING_V2=true 4 | LANGCHAIN_CALLBACKS_BACKGROUND=true 5 | # ----------------------------------------------------- 6 | 7 | OPENAI_API_KEY= 8 | -------------------------------------------------------------------------------- /intro/.env.example: -------------------------------------------------------------------------------- 1 | # ------------------LangSmith tracing------------------ 2 | LANGCHAIN_API_KEY= 3 | LANGCHAIN_TRACING_V2=true 4 | LANGCHAIN_CALLBACKS_BACKGROUND=true 5 | # ----------------------------------------------------- 6 | 7 | TAVILY_API_KEY= 8 | OPENAI_API_KEY= 9 | -------------------------------------------------------------------------------- /streaming_messages_frontend/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import ChatInterface from "../components/ChatInterface"; 2 | 3 | export default function Home() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /stockbroker/backend/.env.example: -------------------------------------------------------------------------------- 1 | # ------------------LangSmith tracing------------------ 2 | LANGCHAIN_API_KEY= 3 | LANGCHAIN_TRACING_V2=true 4 | LANGCHAIN_CALLBACKS_BACKGROUND=true 5 | # ----------------------------------------------------- 6 | 7 | FINANCIAL_DATASETS_API_KEY= 8 | TAVILY_API_KEY= 9 | OPENAI_API_KEY= 10 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { APIDocsAlert } from "@/components/APIDocsAlert"; 2 | import ChatInterface from "../components/ChatInterface"; 3 | 4 | export default function Home() { 5 | return ( 6 |
7 | 8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Message = { 2 | id: string; 3 | text: string; 4 | sender: string; 5 | toolCalls?: ToolCall[]; 6 | }; 7 | 8 | export interface ToolCall { 9 | id: string; 10 | name: string; 11 | args: string; 12 | result?: any; 13 | } 14 | 15 | export type Model = "gpt-4o-mini" | string; // Add other model options as needed 16 | -------------------------------------------------------------------------------- /stockbroker/turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "globalDependencies": ["**/.env"], 4 | "tasks": { 5 | "build": { 6 | "dependsOn": ["^build"], 7 | "outputs": ["**/dist/**"] 8 | }, 9 | "lint": { 10 | "dependsOn": ["^lint"] 11 | }, 12 | "format": { 13 | "dependsOn": ["^format"] 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /stockbroker/frontend/src/utils/cookies.ts: -------------------------------------------------------------------------------- 1 | import Cookies from "js-cookie"; 2 | 3 | export function getCookie(key: string) { 4 | if (typeof window === "undefined") return null; 5 | return Cookies.get(key) ?? null; 6 | } 7 | 8 | export function setCookie(key: string, value: string) { 9 | if (typeof window === "undefined") return null; 10 | Cookies.set(key, value); 11 | } 12 | -------------------------------------------------------------------------------- /streaming_messages_frontend/src/utils/cookies.ts: -------------------------------------------------------------------------------- 1 | import Cookies from "js-cookie"; 2 | 3 | export function getCookie(key: string) { 4 | if (typeof window === "undefined") return null; 5 | return Cookies.get(key) ?? null; 6 | } 7 | 8 | export function setCookie(key: string, value: string) { 9 | if (typeof window === "undefined") return null; 10 | Cookies.set(key, value); 11 | } 12 | -------------------------------------------------------------------------------- /streaming_messages_frontend/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Message = { 2 | id: string; 3 | text?: string; 4 | rawResponse?: Record; 5 | sender: string; 6 | toolCalls?: ToolCall[]; 7 | }; 8 | 9 | export interface ToolCall { 10 | id: string; 11 | name: string; 12 | args: string; 13 | result?: any; 14 | } 15 | 16 | export type Model = "gpt-4o-mini" | string; // Add other model options as needed 17 | -------------------------------------------------------------------------------- /streaming_messages/src/runnable/stream_messages.ts: -------------------------------------------------------------------------------- 1 | import { graph } from "./graph.js"; 2 | 3 | const input = { 4 | messages: { 5 | role: "user", 6 | content: "What is the current stock price of $AAPL?", 7 | }, 8 | }; 9 | 10 | const config = { 11 | configurable: { 12 | thread_id: "stream_messages", 13 | }, 14 | }; 15 | 16 | const stream = await graph.stream(input, config); 17 | 18 | for await (const event of stream) { 19 | console.log(event); 20 | } 21 | -------------------------------------------------------------------------------- /stockbroker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stockbroker-monorepo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "author": "Brace Sproul", 6 | "license": "MIT", 7 | "workspaces": [ 8 | "frontend", 9 | "backend" 10 | ], 11 | "scripts": { 12 | "build": "yarn turbo run build", 13 | "lint": "yarn turbo run lint", 14 | "format": "yarn turbo run format" 15 | }, 16 | "devDependencies": { 17 | "turbo": "latest" 18 | }, 19 | "packageManager": "yarn@1.22.19" 20 | } -------------------------------------------------------------------------------- /streaming_messages/src/runnable/stream_values.ts: -------------------------------------------------------------------------------- 1 | import { graph } from "./graph.js"; 2 | 3 | const input = { 4 | messages: { 5 | role: "user", 6 | content: "What is the current stock price of $AAPL?", 7 | }, 8 | }; 9 | 10 | const config = { 11 | configurable: { 12 | thread_id: "stream_values", 13 | }, 14 | streamMode: "values" as const, 15 | }; 16 | 17 | const stream = await graph.stream(input, config); 18 | 19 | for await (const event of stream) { 20 | console.log(event); 21 | } 22 | -------------------------------------------------------------------------------- /streaming_messages/src/runnable/stream_updates.ts: -------------------------------------------------------------------------------- 1 | import { graph } from "./graph.js"; 2 | 3 | const input = { 4 | messages: { 5 | role: "user", 6 | content: "What is the current stock price of $AAPL?", 7 | }, 8 | }; 9 | 10 | const config = { 11 | configurable: { 12 | thread_id: "stream_updates", 13 | }, 14 | streamMode: "updates" as const, 15 | }; 16 | 17 | const stream = await graph.stream(input, config); 18 | 19 | for await (const event of stream) { 20 | console.log(event); 21 | } 22 | -------------------------------------------------------------------------------- /stockbroker/frontend/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } -------------------------------------------------------------------------------- /stockbroker/frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "printWidth": 80, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "semi": true, 7 | "singleQuote": false, 8 | "quoteProps": "as-needed", 9 | "jsxSingleQuote": false, 10 | "trailingComma": "es5", 11 | "bracketSpacing": true, 12 | "arrowParens": "always", 13 | "requirePragma": false, 14 | "insertPragma": false, 15 | "proseWrap": "preserve", 16 | "htmlWhitespaceSensitivity": "css", 17 | "vueIndentScriptAndStyle": false, 18 | "endOfLine": "lf" 19 | } 20 | -------------------------------------------------------------------------------- /streaming_messages_frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "printWidth": 80, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "semi": true, 7 | "singleQuote": false, 8 | "quoteProps": "as-needed", 9 | "jsxSingleQuote": false, 10 | "trailingComma": "es5", 11 | "bracketSpacing": true, 12 | "arrowParens": "always", 13 | "requirePragma": false, 14 | "insertPragma": false, 15 | "proseWrap": "preserve", 16 | "htmlWhitespaceSensitivity": "css", 17 | "vueIndentScriptAndStyle": false, 18 | "endOfLine": "lf" 19 | } 20 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata: Metadata = { 8 | title: "Streaming UI chat", 9 | description: "Streaming UI chat", 10 | }; 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: Readonly<{ 15 | children: React.ReactNode; 16 | }>) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /stockbroker/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | 39 | -------------------------------------------------------------------------------- /streaming_messages_frontend/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata: Metadata = { 8 | title: "Streaming UI chat", 9 | description: "Streaming UI chat", 10 | }; 11 | 12 | export default function RootLayout({ 13 | children, 14 | }: Readonly<{ 15 | children: React.ReactNode; 16 | }>) { 17 | return ( 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /streaming_messages/src/runnable/stream_events.ts: -------------------------------------------------------------------------------- 1 | import { graph } from "./graph.js"; 2 | 3 | const input = { 4 | messages: { 5 | role: "user", 6 | content: "What is the current stock price of $AAPL?", 7 | }, 8 | }; 9 | 10 | const config = { 11 | configurable: { 12 | thread_id: "stream_events", 13 | }, 14 | version: "v2" as const, 15 | }; 16 | 17 | const stream = graph.streamEvents(input, config); 18 | 19 | for await (const event of stream) { 20 | console.dir( 21 | { 22 | event: event.event, 23 | data: event.data, 24 | }, 25 | { depth: 3 } 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /streaming_messages_frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | 39 | -------------------------------------------------------------------------------- /stockbroker/frontend/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/components/SkeletonMessage.tsx: -------------------------------------------------------------------------------- 1 | export default function SkeletonMessage() { 2 | return ( 3 |
4 | Bot Icon 10 |
11 |
12 |
13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /streaming_messages_frontend/src/components/SkeletonMessage.tsx: -------------------------------------------------------------------------------- 1 | export default function SkeletonMessage() { 2 | return ( 3 |
4 | Bot Icon 10 |
11 |
12 |
13 |
14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /streaming_messages_frontend/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /streaming_messages_frontend/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 13 | "gradient-conic": 14 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 15 | }, 16 | }, 17 | }, 18 | plugins: [require("tailwind-scrollbar-hide")], 19 | }; 20 | export default config; 21 | -------------------------------------------------------------------------------- /streaming_messages_frontend/src/components/MessageList.tsx: -------------------------------------------------------------------------------- 1 | import Message from "./Message"; 2 | import { Message as MessageType } from "../types"; 3 | 4 | export default function MessageList({ messages }: { messages: MessageType[] }) { 5 | return ( 6 |
7 | {messages.map((message, index) => ( 8 |
9 | 15 |
16 | ))} 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /stockbroker/frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /streaming_messages_frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /human_in_the_loop/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/recommended", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "ES2021", 7 | "ES2022.Object", 8 | "DOM" 9 | ], 10 | "target": "ES2021", 11 | "module": "nodenext", 12 | "sourceMap": true, 13 | "allowSyntheticDefaultImports": true, 14 | "baseUrl": "./src", 15 | "declaration": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "noUnusedParameters": true, 19 | "useDefineForClassFields": true, 20 | "strictPropertyInitialization": false 21 | }, 22 | "exclude": [ 23 | "node_modules/", 24 | "dist/", 25 | "tests/" 26 | ], 27 | "include": [ 28 | "./src" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /stockbroker/backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/recommended", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "ES2021", 7 | "ES2022.Object", 8 | "DOM" 9 | ], 10 | "target": "ES2021", 11 | "module": "nodenext", 12 | "sourceMap": true, 13 | "allowSyntheticDefaultImports": true, 14 | "baseUrl": "./src", 15 | "declaration": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "noUnusedParameters": true, 19 | "useDefineForClassFields": true, 20 | "strictPropertyInitialization": false 21 | }, 22 | "exclude": [ 23 | "node_modules/", 24 | "dist/", 25 | "tests/" 26 | ], 27 | "include": [ 28 | "./src" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /streaming_messages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/recommended", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "ES2021", 7 | "ES2022.Object", 8 | "DOM" 9 | ], 10 | "target": "ES2021", 11 | "module": "nodenext", 12 | "sourceMap": true, 13 | "allowSyntheticDefaultImports": true, 14 | "baseUrl": "./src", 15 | "declaration": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "noUnusedParameters": true, 19 | "useDefineForClassFields": true, 20 | "strictPropertyInitialization": false 21 | }, 22 | "exclude": [ 23 | "node_modules/", 24 | "dist/", 25 | "tests/" 26 | ], 27 | "include": [ 28 | "./src" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /intro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/recommended", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "ES2021", 7 | "ES2022.Object", 8 | "DOM" 9 | ], 10 | "target": "ES2021", 11 | "module": "nodenext", 12 | "sourceMap": true, 13 | "strict": true, 14 | "allowSyntheticDefaultImports": true, 15 | "baseUrl": "./src", 16 | "declaration": true, 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | "noUnusedParameters": true, 20 | "useDefineForClassFields": true, 21 | "strictPropertyInitialization": false 22 | }, 23 | "exclude": [ 24 | "node_modules/", 25 | "dist/", 26 | "tests/" 27 | ], 28 | "include": [ 29 | "./src" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/components/MessageList.tsx: -------------------------------------------------------------------------------- 1 | import Message from "./Message"; 2 | import SkeletonMessage from "./SkeletonMessage"; 3 | import { Message as MessageType } from "../types"; 4 | 5 | export default function MessageList({ 6 | messages, 7 | isLoading, 8 | }: { 9 | messages: MessageType[]; 10 | isLoading: boolean; 11 | }) { 12 | return ( 13 |
14 | {messages.map((message, index) => ( 15 |
16 | 21 |
22 | ))} 23 | {isLoading && } 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /streaming_messages/src/langgraph_sdk/stream_values.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "@langchain/langgraph-sdk"; 2 | import { logValuesEvent } from "utils.js"; 3 | 4 | const client = new Client({ 5 | apiKey: process.env.LANGCHAIN_API_KEY, 6 | apiUrl: process.env.LANGGRAPH_API_URL, 7 | }); 8 | 9 | const thread = await client.threads.create(); 10 | const threadId = thread.thread_id; 11 | const assistant = await client.assistants.create({ 12 | graphId: process.env.LANGGRAPH_GRAPH_ID as string, 13 | }); 14 | const assistantId = assistant.assistant_id; 15 | 16 | const input = { 17 | messages: { 18 | role: "user", 19 | content: "What is the current stock price of $AAPL?", 20 | }, 21 | }; 22 | 23 | const stream = client.runs.stream(threadId, assistantId, { 24 | input, 25 | streamMode: "values", 26 | }); 27 | 28 | for await (const event of stream) { 29 | logValuesEvent(event); 30 | } 31 | -------------------------------------------------------------------------------- /streaming_messages/src/langgraph_sdk/stream_updates.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "@langchain/langgraph-sdk"; 2 | import { logUpdateEvent } from "utils.js"; 3 | 4 | const client = new Client({ 5 | apiKey: process.env.LANGCHAIN_API_KEY, 6 | apiUrl: process.env.LANGGRAPH_API_URL, 7 | }); 8 | 9 | const thread = await client.threads.create(); 10 | const threadId = thread.thread_id; 11 | const assistant = await client.assistants.create({ 12 | graphId: process.env.LANGGRAPH_GRAPH_ID as string, 13 | }); 14 | const assistantId = assistant.assistant_id; 15 | 16 | const input = { 17 | messages: { 18 | role: "user", 19 | content: "What is the current stock price of $AAPL?", 20 | }, 21 | }; 22 | 23 | const stream = client.runs.stream(threadId, assistantId, { 24 | input, 25 | streamMode: "updates", 26 | }); 27 | 28 | for await (const event of stream) { 29 | logUpdateEvent(event); 30 | } 31 | -------------------------------------------------------------------------------- /streaming_messages/src/langgraph_sdk/stream_events.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "@langchain/langgraph-sdk"; 2 | 3 | const client = new Client({ 4 | apiKey: process.env.LANGCHAIN_API_KEY, 5 | apiUrl: process.env.LANGGRAPH_API_URL, 6 | }); 7 | 8 | const thread = await client.threads.create(); 9 | const threadId = thread.thread_id; 10 | const assistant = await client.assistants.create({ 11 | graphId: process.env.LANGGRAPH_GRAPH_ID as string, 12 | }); 13 | const assistantId = assistant.assistant_id; 14 | 15 | const input = { 16 | messages: { 17 | role: "user", 18 | content: "What is the current stock price of $AAPL?", 19 | }, 20 | }; 21 | 22 | const stream = client.runs.stream(threadId, assistantId, { 23 | input, 24 | streamMode: "events", 25 | }); 26 | 27 | for await (const event of stream) { 28 | console.log({ 29 | event: event.event, 30 | data: event.data, 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /stockbroker/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "financial-expert", 3 | "description": "Financial expert LangGraph.js example", 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "yarn tsc --project tsconfig.json --outDir dist", 8 | "start": "tsx --experimental-wasm-modules -r dotenv/config src/index.ts", 9 | "format": "prettier --write \"**/*.ts\"", 10 | "format:check": "prettier --check \"**/*.ts\"" 11 | }, 12 | "author": "Brace Sproul", 13 | "license": "MIT", 14 | "dependencies": { 15 | "@langchain/community": "^0.3.34", 16 | "@langchain/core": "^0.3.42", 17 | "@langchain/langgraph": "^0.2.54", 18 | "@langchain/openai": "^0.4.4", 19 | "zod": "^3.23.8" 20 | }, 21 | "devDependencies": { 22 | "@tsconfig/recommended": "^1.0.2", 23 | "dotenv": "^16.0.3", 24 | "prettier": "^2.8.3", 25 | "tsx": "^4.19.0", 26 | "typescript": "^5.0.0" 27 | } 28 | } -------------------------------------------------------------------------------- /streaming_messages_frontend/README.md: -------------------------------------------------------------------------------- 1 | # Streaming Messages 2 | 3 | This project contains a Next.js app, with API routes to hit a LangGraph Cloud deployment and demonstrate exactly how the different streaming types work. 4 | To change the streaming type, click on the settings (⚙️) icon in the top right corner of the app and select the desired streaming type. 5 | 6 | ## [YouTube Video](https://youtu.be/wjn5tFbLgwA) 7 | 8 | ## Setup 9 | 10 | To setup the project, install the dependencies: 11 | 12 | ```bash 13 | yarn install 14 | ``` 15 | 16 | ## Environment variables 17 | 18 | The streaming messages project only requires your LangChain API key, the LangGraph Cloud deployment URL, and the name of your graph. 19 | 20 | Once you have these, create a `.env` file in this directory and add the following: 21 | 22 | ```bash 23 | LANGGRAPH_API_URL=http://localhost:8123 # Or your production URL 24 | LANGCHAIN_API_KEY=YOUR_API_KEY 25 | NEXT_PUBLIC_LANGGRAPH_GRAPH_ID=YOUR_GRAPH_ID 26 | ``` 27 | -------------------------------------------------------------------------------- /intro/README.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | This directory contains a simple LangGraph Graph, built for the introduction video in the LangGraph.js video series. 4 | This directory contains a single graph, located inside the `index.ts` file. 5 | 6 | ## [YouTube Video](https://www.youtube.com/watch?v=Qu8BYTnh3K0) 7 | 8 | ## Setup 9 | 10 | To setup the intro project, install the dependencies: 11 | 12 | ```bash 13 | yarn install 14 | ``` 15 | 16 | ## Environment variables 17 | 18 | The intro project requires Tavily and OpenAI API keys to run. Sign up here: 19 | 20 | - OpenAI: https://platform.openai.com/signup 21 | - Tavily: https://tavily.com/ 22 | 23 | Once you have your API keys, create a `.env` file in this directory and add the following: 24 | 25 | ```bash 26 | TAVILY_API_KEY=YOUR_API_KEY 27 | OPENAI_API_KEY=YOUR_API_KEY 28 | ``` 29 | 30 | ## LangGraph Config 31 | 32 | The LangGraph configuration file for the intro project is located inside [`langgraph.json`](langgraph.json). This file defines the single graph implemented in the project: `simple_agent`. -------------------------------------------------------------------------------- /streaming_messages_frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streaming_chat_frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "format": "prettier --config .prettierrc --write \"src\"" 11 | }, 12 | "dependencies": { 13 | "@langchain/langgraph-sdk": "^0.0.52", 14 | "js-cookie": "^3.0.5", 15 | "next": "14.2.7", 16 | "react": "^18", 17 | "react-dom": "^18", 18 | "react-json-view": "^1.21.3", 19 | "react-markdown": "^9.0.1", 20 | "tailwind-scrollbar-hide": "^1.1.7", 21 | "uuid": "^10.0.0" 22 | }, 23 | "devDependencies": { 24 | "@types/js-cookie": "^3.0.6", 25 | "@types/node": "^20", 26 | "@types/react": "^18", 27 | "@types/react-dom": "^18", 28 | "@types/uuid": "^10.0.0", 29 | "eslint": "^8", 30 | "eslint-config-next": "14.2.7", 31 | "postcss": "^8", 32 | "prettier": "^3.3.3", 33 | "tailwindcss": "^3.4.1", 34 | "typescript": "^5" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /streaming_messages_frontend/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | *::-webkit-scrollbar { 19 | display: none; 20 | } 21 | 22 | body { 23 | color: rgb(var(--foreground-rgb)); 24 | background: linear-gradient( 25 | to bottom, 26 | transparent, 27 | rgb(var(--background-end-rgb)) 28 | ) 29 | rgb(var(--background-start-rgb)); 30 | } 31 | 32 | a { 33 | color: rgb(33, 118, 246); 34 | } 35 | 36 | @layer utilities { 37 | .text-balance { 38 | text-wrap: balance; 39 | } 40 | .no-scrollbar::-webkit-scrollbar { 41 | display: none; 42 | } 43 | 44 | .no-scrollbar { 45 | -ms-overflow-style: none; /* IE and Edge */ 46 | scrollbar-width: none; /* Firefox */ 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /streaming_messages/src/langgraph_sdk/stream_messages.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "@langchain/langgraph-sdk"; 2 | import { logMessageEvent } from "utils.js"; 3 | 4 | const client = new Client({ 5 | apiKey: process.env.LANGCHAIN_API_KEY, 6 | apiUrl: process.env.LANGGRAPH_API_URL, 7 | }); 8 | 9 | const thread = await client.threads.create(); 10 | const threadId = thread.thread_id; 11 | const assistant = await client.assistants.create({ 12 | graphId: process.env.LANGGRAPH_GRAPH_ID as string, 13 | }); 14 | const assistantId = assistant.assistant_id; 15 | 16 | const input = { 17 | messages: { 18 | role: "user", 19 | content: "What is the current stock price of $AAPL?", 20 | }, 21 | }; 22 | 23 | const stream = client.runs.stream(threadId, assistantId, { 24 | input, 25 | streamMode: "messages", 26 | }); 27 | 28 | let toolCallLogged = false; 29 | let contentLogged = false; 30 | 31 | for await (const event of stream) { 32 | const res = logMessageEvent(event, { 33 | toolCallLogged, 34 | contentLogged, 35 | }); 36 | toolCallLogged = res.toolCallLogged; 37 | contentLogged = res.contentLogged; 38 | } 39 | -------------------------------------------------------------------------------- /human_in_the_loop/src/utils.ts: -------------------------------------------------------------------------------- 1 | export const logEvent = (event: Record) => { 2 | const key = Object.keys(event)[0]; 3 | if (key) { 4 | console.log(`Event: ${key}`); 5 | if (Array.isArray(event[key].messages)) { 6 | const lastMsg = event[key].messages[event[key].messages.length - 1]; 7 | console.log( 8 | { 9 | role: lastMsg._getType(), 10 | content: lastMsg.content, 11 | }, 12 | "\n" 13 | ); 14 | } else { 15 | console.log( 16 | { 17 | role: event[key].messages._getType(), 18 | content: event[key].messages.content, 19 | }, 20 | "\n" 21 | ); 22 | } 23 | } 24 | }; 25 | 26 | export const logStateUpdate = (newBuildup: string, newPunchline: string) => { 27 | console.log( 28 | "Updating state to:", 29 | { 30 | buildup: newBuildup, 31 | punchline: newPunchline, 32 | }, 33 | "\n" 34 | ); 35 | }; 36 | 37 | export const logLastMessageToolCalls = (lastMessage: Record) => { 38 | console.log("Last message tool calls:", { 39 | name: lastMessage.tool_calls[0].name, 40 | args: lastMessage.tool_calls[0].args, 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /stockbroker/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streaming_chat_frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "format": "prettier --config .prettierrc --write \"src\"" 11 | }, 12 | "dependencies": { 13 | "@langchain/langgraph-sdk": "^0.0.8", 14 | "@radix-ui/react-icons": "^1.3.0", 15 | "class-variance-authority": "^0.7.0", 16 | "clsx": "^2.1.1", 17 | "js-cookie": "^3.0.5", 18 | "lucide-react": "^0.438.0", 19 | "next": "14.2.7", 20 | "react": "^18", 21 | "react-dom": "^18", 22 | "react-json-view": "^1.21.3", 23 | "react-markdown": "^9.0.1", 24 | "tailwind-merge": "^2.5.2", 25 | "tailwind-scrollbar-hide": "^1.1.7", 26 | "tailwindcss-animate": "^1.0.7", 27 | "uuid": "^10.0.0" 28 | }, 29 | "devDependencies": { 30 | "@types/js-cookie": "^3.0.6", 31 | "@types/node": "^20", 32 | "@types/react": "^18", 33 | "@types/react-dom": "^18", 34 | "@types/uuid": "^10.0.0", 35 | "eslint": "^8", 36 | "eslint-config-next": "14.2.7", 37 | "postcss": "^8", 38 | "prettier": "^3.3.3", 39 | "tailwindcss": "^3.4.1", 40 | "typescript": "^5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/components/HomeComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Image from "next/image"; 3 | 4 | const exampleMessages = [ 5 | "What're some facts about Google?", 6 | "How much revenue did Apple make last year?", 7 | "Is McDonald's profitable?", 8 | "What's the current stock price of Tesla?", 9 | ]; 10 | 11 | const HomeComponent: React.FC<{ 12 | onMessageSelect: (message: string) => void; 13 | }> = ({ onMessageSelect }) => { 14 | return ( 15 |
16 | StreamChat 23 |
24 | {exampleMessages.map((message, index) => ( 25 |
onMessageSelect(message)} 29 | > 30 | {message} 31 |
32 | ))} 33 |
34 |
35 | ); 36 | }; 37 | 38 | export default HomeComponent; 39 | -------------------------------------------------------------------------------- /streaming_messages_frontend/src/components/HomeComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Image from "next/image"; 3 | 4 | const exampleMessages = [ 5 | "What're some facts about Google?", 6 | "How much revenue did Apple make last year?", 7 | "Is McDonald's profitable?", 8 | "What's the current stock price of Tesla?", 9 | ]; 10 | 11 | const HomeComponent: React.FC<{ 12 | onMessageSelect: (message: string) => void; 13 | }> = ({ onMessageSelect }) => { 14 | return ( 15 |
16 | StreamChat 23 |
24 | {exampleMessages.map((message, index) => ( 25 |
onMessageSelect(message)} 29 | > 30 | {message} 31 |
32 | ))} 33 |
34 |
35 | ); 36 | }; 37 | 38 | export default HomeComponent; 39 | -------------------------------------------------------------------------------- /stockbroker/frontend/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /streaming_messages_frontend/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /intro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "financial-expert", 3 | "description": "Financial expert LangGraph.js example", 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "yarn tsc --project tsconfig.json --outDir dist", 8 | "start": "tsx --experimental-wasm-modules -r dotenv/config src/index.ts", 9 | "lint": "eslint src", 10 | "lint:fix": "yarn lint --fix", 11 | "format": "prettier --write \"**/*.ts\"", 12 | "format:check": "prettier --check \"**/*.ts\"" 13 | }, 14 | "author": "Brace Sproul", 15 | "license": "MIT", 16 | "dependencies": { 17 | "@langchain/community": "^0.3.34", 18 | "@langchain/core": "^0.3.42", 19 | "@langchain/langgraph": "^0.2.54", 20 | "@langchain/openai": "^0.4.4", 21 | "zod": "^3.23.8" 22 | }, 23 | "devDependencies": { 24 | "@tsconfig/recommended": "^1.0.2", 25 | "@typescript-eslint/eslint-plugin": "^5.51.0", 26 | "@typescript-eslint/parser": "^5.51.0", 27 | "dotenv": "^16.0.3", 28 | "eslint": "^8.33.0", 29 | "eslint-config-airbnb-base": "^15.0.0", 30 | "eslint-config-prettier": "^8.6.0", 31 | "eslint-plugin-import": "^2.27.5", 32 | "eslint-plugin-prettier": "^4.2.1", 33 | "eslint-plugin-unused-imports": "^3.0.0", 34 | "prettier": "^2.8.3", 35 | "tsx": "^3.12.3", 36 | "typescript": "^5.0.0" 37 | }, 38 | "resolutions": { 39 | "@langchain/core": "0.2.31" 40 | } 41 | } -------------------------------------------------------------------------------- /human_in_the_loop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "human-in-the-loop", 3 | "description": "LangGraph.js examples of human in the loop graphs.", 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "yarn tsc --project tsconfig.json --outDir dist", 8 | "start:hitl": "tsx --experimental-wasm-modules -r dotenv/config src/human_in_the_loop.ts", 9 | "start:dynamic_breakpoints": "tsx --experimental-wasm-modules -r dotenv/config src/dynamic_breakpoints.ts", 10 | "lint": "eslint src", 11 | "lint:fix": "yarn lint --fix", 12 | "format": "prettier --write \"**/*.ts\"", 13 | "format:check": "prettier --check \"**/*.ts\"" 14 | }, 15 | "author": "Brace Sproul", 16 | "license": "MIT", 17 | "dependencies": { 18 | "@langchain/community": "^0.3.34", 19 | "@langchain/core": "^0.3.42", 20 | "@langchain/langgraph": "^0.2.54", 21 | "@langchain/openai": "^0.4.4", 22 | "zod": "^3.23.8" 23 | }, 24 | "devDependencies": { 25 | "@tsconfig/recommended": "^1.0.2", 26 | "@typescript-eslint/eslint-plugin": "^5.51.0", 27 | "@typescript-eslint/parser": "^5.51.0", 28 | "dotenv": "^16.0.3", 29 | "eslint": "^8.33.0", 30 | "eslint-config-airbnb-base": "^15.0.0", 31 | "eslint-config-prettier": "^8.6.0", 32 | "eslint-plugin-import": "^2.27.5", 33 | "eslint-plugin-prettier": "^4.2.1", 34 | "eslint-plugin-unused-imports": "^3.0.0", 35 | "prettier": "^2.8.3", 36 | "tsx": "^3.12.3", 37 | "typescript": "^5.0.0" 38 | }, 39 | "resolutions": { 40 | "@langchain/core": "0.2.31" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/components/Message.tsx: -------------------------------------------------------------------------------- 1 | import Markdown from "react-markdown"; 2 | import ToolCall from "./ToolCall"; 3 | import { ToolCall as ToolCallType } from "../types"; 4 | import { useState, useEffect } from "react"; 5 | 6 | export default function Message({ 7 | text, 8 | sender, 9 | toolCalls, 10 | }: { 11 | text: string; 12 | sender: string; 13 | toolCalls?: ToolCallType[]; 14 | }) { 15 | const isBot = sender === "ai"; 16 | const [isVisible, setIsVisible] = useState(false); 17 | 18 | useEffect(() => { 19 | setIsVisible(true); 20 | }, []); 21 | 22 | return ( 23 |
30 | {isBot && ( 31 | Bot Icon 37 | )} 38 |
45 | {toolCalls && 46 | toolCalls.length > 0 && 47 | toolCalls.map((toolCall) => ( 48 | 49 | ))} 50 | {isBot ? {text} : text} 51 |
52 |
53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LangGraph.js Examples 2 | 3 | This repository contains a series of example TypeScript projects which implement LangGraph.js agents. 4 | Each directory focuses on a different problem which LangGraph.js aims to solve/enable solutions for. 5 | 6 | ## Prerequisites 7 | 8 | The following projects all use [LangSmith](https://smith.langchain.com/), LangGraph [Studio](https://github.com/langchain-ai/langgraph-studio) and [Cloud](https://langchain-ai.github.io/langgraph/cloud/), as well as the [LangGraph.js](https://langchain-ai.github.io/langgraphjs/) and [LangChain.js](https://js.langchain.com/v0.2/docs/introduction/) libraries. 9 | 10 | Before jumping into any of the projects, you should create a LangSmith account [here](https://smith.langchain.com/), and download the latest LangGraph Studio version [here](https://github.com/langchain-ai/langgraph-studio/releases/latest). 11 | 12 | Running LangGraph Studio locally requires [Docker](https://www.docker.com/), so ensure you have it installed _and_ running before starting the Studio (I personally use [OrbStack](https://orbstack.dev/) to manage my Docker containers, which is free to use for personal use). 13 | 14 | ## Projects 15 | 16 | - [Intro](./intro/README.md) - Introduction to LangGraph.js, Studio, and Cloud. 17 | - [Human in the Loop](./human_in_the_loop/README.md) - Introduction to Human in the Loop (HITL) concepts. 18 | - [Stockbroker](./stockbroker/README.md) - A full stack stockbroker & financial analyst app, with HITL for purchasing stocks. 19 | - Streaming Messages ([Examples](./streaming_messages/README.md), [Frontend](./streaming_messages_frontend/README.md)) - Next.js web app connected to a LangGraph Cloud deployment to show off different message streaming types. 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .node_modules/ 3 | built/* 4 | tests/cases/rwc/* 5 | tests/cases/perf/* 6 | !tests/cases/webharness/compilerToString.js 7 | test-args.txt 8 | ~*.docx 9 | \#*\# 10 | .\#* 11 | tests/baselines/local/* 12 | tests/baselines/local.old/* 13 | tests/services/baselines/local/* 14 | tests/baselines/prototyping/local/* 15 | tests/baselines/rwc/* 16 | tests/baselines/reference/projectOutput/* 17 | tests/baselines/local/projectOutput/* 18 | tests/baselines/reference/testresults.tap 19 | tests/services/baselines/prototyping/local/* 20 | tests/services/browser/typescriptServices.js 21 | src/harness/*.js 22 | src/compiler/diagnosticInformationMap.generated.ts 23 | src/compiler/diagnosticMessages.generated.json 24 | src/parser/diagnosticInformationMap.generated.ts 25 | src/parser/diagnosticMessages.generated.json 26 | rwc-report.html 27 | *.swp 28 | build.json 29 | *.actual 30 | tests/webTestServer.js 31 | tests/webTestServer.js.map 32 | tests/webhost/*.d.ts 33 | tests/webhost/webtsc.js 34 | tests/cases/**/*.js 35 | tests/cases/**/*.js.map 36 | *.config 37 | scripts/eslint/built/ 38 | scripts/debug.bat 39 | scripts/run.bat 40 | scripts/**/*.js 41 | scripts/**/*.js.map 42 | coverage/ 43 | internal/ 44 | **/.DS_Store 45 | .settings 46 | **/.vs 47 | **/.vscode/* 48 | !**/.vscode/tasks.json 49 | !**/.vscode/settings.template.json 50 | !**/.vscode/launch.template.json 51 | !**/.vscode/extensions.json 52 | !tests/cases/projects/projectOption/**/node_modules 53 | !tests/cases/projects/NodeModulesSearch/**/* 54 | !tests/baselines/reference/project/nodeModules*/**/* 55 | .idea 56 | yarn-error.log 57 | .parallelperf.* 58 | tests/baselines/reference/dt 59 | .failed-tests 60 | TEST-results.xml 61 | package-lock.json 62 | .eslintcache 63 | *v8.log 64 | /lib/ 65 | dist 66 | .turbo 67 | .env -------------------------------------------------------------------------------- /streaming_messages_frontend/src/components/InputArea.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export default function InputArea({ 4 | onSendMessage, 5 | }: { 6 | onSendMessage: (message: string) => void; 7 | }) { 8 | const [input, setInput] = useState(""); 9 | 10 | const handleSubmit = (e: React.FormEvent) => { 11 | e.preventDefault(); 12 | if (input.trim()) { 13 | onSendMessage(input); 14 | setInput(""); 15 | } 16 | }; 17 | 18 | return ( 19 |
20 |
21 | setInput(e.target.value)} 25 | className=" h-full w-full rounded-[30px] px-10 focus:outline-none bg-[#2f2f2f] placeholder-white text-white" 26 | placeholder="Message StreamChat" 27 | /> 28 | 47 |
48 |
49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /streaming_messages/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "human-in-the-loop", 3 | "description": "LangGraph.js examples of human in the loop graphs.", 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "build": "yarn tsc --project tsconfig.json --outDir dist", 8 | "start:events": "tsx --experimental-wasm-modules -r dotenv/config src/runnable/stream_events.ts", 9 | "start:messages": "tsx --experimental-wasm-modules -r dotenv/config src/runnable/stream_messages.ts", 10 | "start:updates": "tsx --experimental-wasm-modules -r dotenv/config src/runnable/stream_updates.ts", 11 | "start:values": "tsx --experimental-wasm-modules -r dotenv/config src/runnable/stream_values.ts", 12 | "lint": "eslint src", 13 | "lint:fix": "yarn lint --fix", 14 | "format": "prettier --write \"**/*.ts\"", 15 | "format:check": "prettier --check \"**/*.ts\"" 16 | }, 17 | "author": "Brace Sproul", 18 | "license": "MIT", 19 | "dependencies": { 20 | "@langchain/community": "^0.3.34", 21 | "@langchain/core": "^0.3.42", 22 | "@langchain/langgraph": "^0.2.54", 23 | "@langchain/langgraph-sdk": "^0.0.52", 24 | "@langchain/openai": "^0.4.4" 25 | }, 26 | "devDependencies": { 27 | "@tsconfig/recommended": "^1.0.2", 28 | "@types/node": "^22.5.3", 29 | "@typescript-eslint/eslint-plugin": "^5.51.0", 30 | "@typescript-eslint/parser": "^5.51.0", 31 | "dotenv": "^16.0.3", 32 | "eslint": "^8.33.0", 33 | "eslint-config-airbnb-base": "^15.0.0", 34 | "eslint-config-prettier": "^8.6.0", 35 | "eslint-plugin-import": "^2.27.5", 36 | "eslint-plugin-prettier": "^4.2.1", 37 | "eslint-plugin-unused-imports": "^3.0.0", 38 | "prettier": "^2.8.3", 39 | "tsx": "^3.12.3", 40 | "typescript": "^5.0.0" 41 | }, 42 | "resolutions": { 43 | "@langchain/core": "0.2.31" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cva, type VariantProps } from "class-variance-authority"; 3 | 4 | import { cn } from "@/utils/shadcnUtils"; 5 | 6 | const alertVariants = cva( 7 | "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", 8 | { 9 | variants: { 10 | variant: { 11 | default: "bg-background text-foreground", 12 | destructive: 13 | "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", 14 | }, 15 | }, 16 | defaultVariants: { 17 | variant: "default", 18 | }, 19 | } 20 | ); 21 | 22 | const Alert = React.forwardRef< 23 | HTMLDivElement, 24 | React.HTMLAttributes & VariantProps 25 | >(({ className, variant, ...props }, ref) => ( 26 |
32 | )); 33 | Alert.displayName = "Alert"; 34 | 35 | const AlertTitle = React.forwardRef< 36 | HTMLParagraphElement, 37 | React.HTMLAttributes 38 | >(({ className, ...props }, ref) => ( 39 |
44 | )); 45 | AlertTitle.displayName = "AlertTitle"; 46 | 47 | const AlertDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |
56 | )); 57 | AlertDescription.displayName = "AlertDescription"; 58 | 59 | export { Alert, AlertTitle, AlertDescription }; 60 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/utils/chatApi.ts: -------------------------------------------------------------------------------- 1 | import { ThreadState, Client } from "@langchain/langgraph-sdk"; 2 | 3 | const createClient = () => { 4 | const apiUrl = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:3000/api"; 5 | return new Client({ 6 | apiUrl, 7 | }); 8 | }; 9 | 10 | export const createAssistant = async (graphId: string) => { 11 | const client = createClient(); 12 | return client.assistants.create({ graphId }); 13 | }; 14 | 15 | export const createThread = async () => { 16 | const client = createClient(); 17 | return client.threads.create(); 18 | }; 19 | 20 | export const getThreadState = async ( 21 | threadId: string 22 | ): Promise>> => { 23 | const client = createClient(); 24 | return client.threads.getState(threadId); 25 | }; 26 | 27 | export const updateState = async ( 28 | threadId: string, 29 | fields: { 30 | newState: Record; 31 | asNode?: string; 32 | } 33 | ) => { 34 | const client = createClient(); 35 | return client.threads.updateState(threadId, { 36 | values: fields.newState, 37 | asNode: fields.asNode, 38 | }); 39 | }; 40 | 41 | export const sendMessage = async (params: { 42 | threadId: string; 43 | assistantId: string; 44 | message: string | null; 45 | model: string; 46 | userId: string; 47 | systemInstructions: string; 48 | }) => { 49 | const client = createClient(); 50 | 51 | let input: Record | null = null; 52 | if (params.message !== null) { 53 | input = { 54 | messages: [ 55 | { 56 | role: "human", 57 | content: params.message, 58 | }, 59 | ], 60 | userId: params.userId, 61 | }; 62 | } 63 | const config = { 64 | configurable: { 65 | model_name: params.model, 66 | system_instructions: params.systemInstructions, 67 | }, 68 | }; 69 | 70 | return client.runs.stream(params.threadId, params.assistantId, { 71 | input, 72 | config, 73 | streamMode: "messages", 74 | }); 75 | }; 76 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/components/InputArea.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export default function InputArea({ 4 | onSendMessage, 5 | disabled, 6 | }: { 7 | onSendMessage: (message: string) => void; 8 | disabled: boolean; 9 | }) { 10 | const [input, setInput] = useState(""); 11 | 12 | const handleSubmit = (e: React.FormEvent) => { 13 | if (disabled) return; 14 | e.preventDefault(); 15 | if (input.trim()) { 16 | onSendMessage(input); 17 | setInput(""); 18 | } 19 | }; 20 | 21 | return ( 22 |
23 |
24 | setInput(e.target.value)} 28 | className=" h-full w-full rounded-[30px] px-10 focus:outline-none bg-[#2f2f2f] placeholder-white text-white" 29 | placeholder="Message StreamChat" 30 | /> 31 | 56 |
57 |
58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /stockbroker/frontend/src/components/Settings.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export type Model = "gpt-4o" | "haiku" | "gpt-4o-mini" | "sonnet-3.5"; 4 | 5 | interface SettingsProps { 6 | onModelChange: (model: Model) => void; 7 | onSystemInstructionsChange: (instructions: string) => void; 8 | currentModel: Model; 9 | currentSystemInstructions: string; 10 | } 11 | 12 | export default function Settings({ 13 | onModelChange, 14 | onSystemInstructionsChange, 15 | currentModel, 16 | currentSystemInstructions, 17 | }: SettingsProps) { 18 | const [isOpen, setIsOpen] = useState(false); 19 | const models: Model[] = ["gpt-4o", "haiku", "gpt-4o-mini", "sonnet-3.5"]; 20 | 21 | return ( 22 |
23 | 29 | {isOpen && ( 30 |
31 |

Model

32 | {models.map((model) => ( 33 | 47 | ))} 48 |

System Instructions

49 |