diff --git a/README.md b/README.md index 554db57..475c162 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## TypeScript Email Assistant Implementation -The TypeScript Email Assistant (`app/page.tsx`) implements an LLM-powered workflow to process emails: +This is a vanilla TypeScript project for email assistant workflows using LangChain and LangGraph. ### Architecture - Uses `StateGraph` from LangGraph to create a multi-step workflow @@ -43,17 +43,37 @@ The TypeScript Email Assistant (`app/page.tsx`) implements an LLM-powered workfl - Properly typed with TypeScript for complete type safety - Uses command pattern to transition between states -### P2 -- [ ] notebooks -- [ ] video course [1,2,3,4] +### Project Structure +- `scripts/`: TypeScript scripts to run the email assistant +- `lib/`: Utility functions, tools, and shared types + - `lib/tools/`: Tool implementations + - `lib/prompts.ts`: Prompt templates + - `lib/schemas.ts`: TypeScript/Zod schemas + - `lib/utils.ts`: Utility functions -### New TS files -- `lib/tools/` -- `lib/prompts.ts` -- `lib/schemas.ts` -- `lib/utils.ts` +## Getting Started -### Course outline +First, install dependencies: + +```bash +npm install +# or +yarn +# or +pnpm install +``` + +Then, run the development script: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +``` + +## Course outline > BUILD > EVAL diff --git a/app/api/email-assistant/route.ts b/app/api/email-assistant/route.ts deleted file mode 100644 index 7047478..0000000 --- a/app/api/email-assistant/route.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { getEmailAssistant } from "../../page"; -import { EmailData } from "../../../lib/schemas"; - -// This is a temporary API route for testing the email assistant (before setting up LangSmith) -export async function POST(request: NextRequest) { - try { - const data = await request.json(); - const { email_input } = data; - - // Get the email assistant - const emailAssistant = await getEmailAssistant(); - - // Run the email assistant with the raw email text - const result = await emailAssistant.invoke({ - email_input - }); - - return NextResponse.json(result); - } catch (error: any) { - console.error("Error running email assistant:", error); - return NextResponse.json( - { error: error.message }, - { status: 500 } - ); - } -} \ No newline at end of file diff --git a/app/favicon.ico b/app/favicon.ico deleted file mode 100644 index 718d6fe..0000000 Binary files a/app/favicon.ico and /dev/null differ diff --git a/app/globals.css b/app/globals.css deleted file mode 100644 index dc98be7..0000000 --- a/app/globals.css +++ /dev/null @@ -1,122 +0,0 @@ -@import "tailwindcss"; -@import "tw-animate-css"; - -@custom-variant dark (&:is(.dark *)); - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); - --color-sidebar-ring: var(--sidebar-ring); - --color-sidebar-border: var(--sidebar-border); - --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); - --color-sidebar-accent: var(--sidebar-accent); - --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); - --color-sidebar-primary: var(--sidebar-primary); - --color-sidebar-foreground: var(--sidebar-foreground); - --color-sidebar: var(--sidebar); - --color-chart-5: var(--chart-5); - --color-chart-4: var(--chart-4); - --color-chart-3: var(--chart-3); - --color-chart-2: var(--chart-2); - --color-chart-1: var(--chart-1); - --color-ring: var(--ring); - --color-input: var(--input); - --color-border: var(--border); - --color-destructive: var(--destructive); - --color-accent-foreground: var(--accent-foreground); - --color-accent: var(--accent); - --color-muted-foreground: var(--muted-foreground); - --color-muted: var(--muted); - --color-secondary-foreground: var(--secondary-foreground); - --color-secondary: var(--secondary); - --color-primary-foreground: var(--primary-foreground); - --color-primary: var(--primary); - --color-popover-foreground: var(--popover-foreground); - --color-popover: var(--popover); - --color-card-foreground: var(--card-foreground); - --color-card: var(--card); - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); -} - -:root { - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.205 0 0); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); -} - -.dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.922 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); -} - -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground; - } -} diff --git a/app/layout.tsx b/app/layout.tsx deleted file mode 100644 index f7fa87e..0000000 --- a/app/layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - -export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/app/test-email-assistant/page.tsx b/app/test-email-assistant/page.tsx deleted file mode 100644 index b398167..0000000 --- a/app/test-email-assistant/page.tsx +++ /dev/null @@ -1,123 +0,0 @@ -"use client"; -// this is a temporary page for testing the email assistant (before setting up LangSmith) -import { useState } from "react"; -import { EmailData } from "../../lib/schemas"; -import { BaseMessage } from "@langchain/core/messages"; - -export default function TestEmailAssistant() { - const [emailInput, setEmailInput] = useState( - `From: john@example.com -To: assistant@company.com -Subject: Meeting Request - -Hi there, - -Can we schedule a meeting for tomorrow at 2pm to discuss the project? - -Thanks, -John` - ); - - const [output, setOutput] = useState({}); - const [loading, setLoading] = useState(false); - const [messages, setMessages] = useState([]); - - async function runTest() { - setLoading(true); - try { - // Call the API route instead of directly invoking the assistant - const response = await fetch('/api/email-assistant', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - email_input: emailInput - }), - }); - - if (!response.ok) { - throw new Error(`API responded with status: ${response.status}`); - } - - const result = await response.json(); - - setOutput(result); - - // Extract messages if they exist - if (result.messages && Array.isArray(result.messages)) { - setMessages(result.messages); - } - } catch (error: any) { - console.error("Error running email assistant:", error); - setOutput({ error: error.message }); - } finally { - setLoading(false); - } - } - - return ( -
-

Email Assistant Test

- -
- -