19 | 🤖 AI Agent Demo 20 |
21 |22 | Ask me math questions, check the weather, or even get a programming 23 | joke! 24 |
25 | 26 |{answer}
45 |├── agent-complete ├── .env.example ├── src │ └── app │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── api │ │ └── agent │ │ ├── tools.ts │ │ └── route.ts ├── postcss.config.mjs ├── public │ ├── vercel.svg │ ├── window.svg │ ├── file.svg │ ├── globe.svg │ └── next.svg ├── next.config.ts ├── eslint.config.mjs ├── .gitignore ├── package.json ├── tsconfig.json └── README.md ├── agent-starter ├── .env.example ├── postcss.config.mjs ├── src │ └── app │ │ ├── favicon.ico │ │ ├── api │ │ └── agent │ │ │ ├── tools.ts │ │ │ └── route.ts │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── snippets.md ├── public │ ├── vercel.svg │ ├── window.svg │ ├── file.svg │ ├── globe.svg │ └── next.svg ├── next.config.ts ├── eslint.config.mjs ├── .gitignore ├── package.json ├── tsconfig.json └── README.md └── README.md /agent-complete/.env.example: -------------------------------------------------------------------------------- 1 | # Copy this file to .env.local and add your key 2 | OPENAI_API_KEY=sk-your-key-here 3 | -------------------------------------------------------------------------------- /agent-starter/.env.example: -------------------------------------------------------------------------------- 1 | # Copy this file to .env.local and add your key 2 | OPENAI_API_KEY=sk-your-key-here 3 | -------------------------------------------------------------------------------- /agent-complete/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/ai-agent-tutorial/main/agent-complete/src/app/favicon.ico -------------------------------------------------------------------------------- /agent-starter/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /agent-starter/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/ai-agent-tutorial/main/agent-starter/src/app/favicon.ico -------------------------------------------------------------------------------- /agent-complete/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /agent-complete/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent-starter/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent-complete/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /agent-starter/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /agent-starter/src/app/api/agent/tools.ts: -------------------------------------------------------------------------------- 1 | // app/api/agent/tools.ts 2 | 3 | import type { ChatCompletionTool } from "openai/resources/chat/completions"; 4 | 5 | // Tools available to the AI agent 6 | export const tools: ChatCompletionTool[] = []; 7 | 8 | // Tool implementations (to be added later) 9 | export async function runTool(name: string, args: any) { 10 | return "Unknown tool"; 11 | } 12 | -------------------------------------------------------------------------------- /agent-complete/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent-starter/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent-complete/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent-starter/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent-complete/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | :root { 4 | --background: #ffffff; 5 | --foreground: #171717; 6 | } 7 | 8 | @theme inline { 9 | --color-background: var(--background); 10 | --color-foreground: var(--foreground); 11 | --font-sans: var(--font-geist-sans); 12 | --font-mono: var(--font-geist-mono); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | :root { 17 | --background: #0a0a0a; 18 | --foreground: #ededed; 19 | } 20 | } 21 | 22 | body { 23 | background: var(--background); 24 | color: var(--foreground); 25 | font-family: Arial, Helvetica, sans-serif; 26 | } 27 | -------------------------------------------------------------------------------- /agent-starter/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | :root { 4 | --background: #ffffff; 5 | --foreground: #171717; 6 | } 7 | 8 | @theme inline { 9 | --color-background: var(--background); 10 | --color-foreground: var(--foreground); 11 | --font-sans: var(--font-geist-sans); 12 | --font-mono: var(--font-geist-mono); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | :root { 17 | --background: #0a0a0a; 18 | --foreground: #ededed; 19 | } 20 | } 21 | 22 | body { 23 | background: var(--background); 24 | color: var(--foreground); 25 | font-family: Arial, Helvetica, sans-serif; 26 | } 27 | -------------------------------------------------------------------------------- /agent-complete/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | { 15 | ignores: [ 16 | "node_modules/**", 17 | ".next/**", 18 | "out/**", 19 | "build/**", 20 | "next-env.d.ts", 21 | ], 22 | }, 23 | ]; 24 | 25 | export default eslintConfig; 26 | -------------------------------------------------------------------------------- /agent-starter/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | { 15 | ignores: [ 16 | "node_modules/**", 17 | ".next/**", 18 | "out/**", 19 | "build/**", 20 | "next-env.d.ts", 21 | ], 22 | }, 23 | ]; 24 | 25 | export default eslintConfig; 26 | -------------------------------------------------------------------------------- /agent-complete/.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.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | !.env.example 36 | 37 | # vercel 38 | .vercel 39 | 40 | # typescript 41 | *.tsbuildinfo 42 | next-env.d.ts 43 | -------------------------------------------------------------------------------- /agent-starter/.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.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | !.env.example 36 | 37 | # vercel 38 | .vercel 39 | 40 | # typescript 41 | *.tsbuildinfo 42 | next-env.d.ts 43 | -------------------------------------------------------------------------------- /agent-starter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "agent-starter", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "eslint" 10 | }, 11 | "dependencies": { 12 | "next": "15.5.4", 13 | "openai": "^5.23.1", 14 | "react": "19.1.0", 15 | "react-dom": "19.1.0" 16 | }, 17 | "devDependencies": { 18 | "@eslint/eslintrc": "^3", 19 | "@tailwindcss/postcss": "^4", 20 | "@types/node": "^20", 21 | "@types/react": "^19", 22 | "@types/react-dom": "^19", 23 | "eslint": "^9", 24 | "eslint-config-next": "15.5.4", 25 | "tailwindcss": "^4", 26 | "typescript": "^5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /agent-complete/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "agent-complete", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "eslint" 10 | }, 11 | "dependencies": { 12 | "next": "15.5.4", 13 | "openai": "^5.23.1", 14 | "react": "19.1.0", 15 | "react-dom": "19.1.0" 16 | }, 17 | "devDependencies": { 18 | "@eslint/eslintrc": "^3", 19 | "@tailwindcss/postcss": "^4", 20 | "@types/node": "^20", 21 | "@types/react": "^19", 22 | "@types/react-dom": "^19", 23 | "eslint": "^9", 24 | "eslint-config-next": "15.5.4", 25 | "tailwindcss": "^4", 26 | "typescript": "^5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /agent-complete/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /agent-starter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /agent-complete/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const geistSans = Geist({ 6 | variable: "--font-geist-sans", 7 | subsets: ["latin"], 8 | }); 9 | 10 | const geistMono = Geist_Mono({ 11 | variable: "--font-geist-mono", 12 | subsets: ["latin"], 13 | }); 14 | 15 | export const metadata: Metadata = { 16 | title: "Create Next App", 17 | description: "Generated by create next app", 18 | }; 19 | 20 | export default function RootLayout({ 21 | children, 22 | }: Readonly<{ 23 | children: React.ReactNode; 24 | }>) { 25 | return ( 26 | 27 |
30 | {children} 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /agent-starter/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const geistSans = Geist({ 6 | variable: "--font-geist-sans", 7 | subsets: ["latin"], 8 | }); 9 | 10 | const geistMono = Geist_Mono({ 11 | variable: "--font-geist-mono", 12 | subsets: ["latin"], 13 | }); 14 | 15 | export const metadata: Metadata = { 16 | title: "Create Next App", 17 | description: "Generated by create next app", 18 | }; 19 | 20 | export default function RootLayout({ 21 | children, 22 | }: Readonly<{ 23 | children: React.ReactNode; 24 | }>) { 25 | return ( 26 | 27 | 30 | {children} 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /agent-starter/src/app/api/agent/route.ts: -------------------------------------------------------------------------------- 1 | // app/api/agent/route.ts 2 | import { NextResponse } from "next/server"; 3 | import OpenAI from "openai"; 4 | import { tools, runTool } from "./tools"; 5 | 6 | const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); 7 | 8 | export async function POST(req: Request) { 9 | try { 10 | const { query } = await req.json(); 11 | // TODO: Call OpenAI with tools 12 | // Step 1: Ask GPT with tools 13 | // Step 2: If GPT calls a tool → run it 14 | // Step 3: Send result back for reasoning 15 | // Step 4: Return final answer 16 | 17 | // Placeholder response until the agent logic is implemented 18 | return NextResponse.json({ answer: "Agent not implemented yet" }); 19 | } catch (err: any) { 20 | console.error("API error:", err); 21 | return NextResponse.json( 22 | { error: err.message || "Server error" }, 23 | { status: 500 } 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /agent-complete/public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent-starter/public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent-complete/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /agent-starter/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 OpenAI Agent Tutorial – Starter Repo 2 | 3 | This is the **starter code** for my YouTube tutorial: 4 | 👉 _“Your First AI Agent (Next.js + OpenAI Tool Calling)”_ 5 | 6 | We’ll start from a fresh **Next.js 15 project** with: 7 | 8 | - ✅ TypeScript 9 | - ✅ Tailwind CSS 10 | - ✅ App Router 11 | - ✅ `@/*` import alias 12 | 13 | In the tutorial, we’ll add the code that turns this into a working **AI Agent**. 14 | 15 | --- 16 | 17 | ## 📦 Setup 18 | 19 | Clone this repo: 20 | 21 | ```bash 22 | git clone https://github.com/jspruance/ai-agent-tutorial.git 23 | cd openai-agent-tutorial-starter 24 | ``` 25 | 26 | Install dependencies: 27 | 28 | ```bash 29 | npm install 30 | ``` 31 | 32 | Add your OpenAI API key (not yet used, but needed later): 33 | 34 | ```bash 35 | # .env.local 36 | OPENAI_API_KEY=your_api_key_here 37 | ``` 38 | 39 | --- 40 | 41 | ## ▶️ Run the Dev Server 42 | 43 | ```bash 44 | npm run dev 45 | ``` 46 | 47 | Then open [http://localhost:3000](http://localhost:3000) in your browser. 48 | 49 | You should see a **blank Next.js app** with Tailwind installed. 50 | 51 | --- 52 | 53 | ## 🗂️ Project Structure 54 | 55 | ``` 56 | src/ 57 | └── app/ 58 | ├── page.tsx # Simple placeholder page 59 | └── api/ # Agent code will go here in the tutorial 60 | ``` 61 | 62 | --- 63 | 64 | ## 📚 References 65 | 66 | - [Next.js App Router Docs](https://nextjs.org/docs/app) 67 | - [Tailwind CSS Docs](https://tailwindcss.com/docs/guides/nextjs) 68 | - [OpenAI Docs](https://platform.openai.com/docs/guides/function-calling) 69 | 70 | --- 71 | 72 | 👉 This repo is just the **starting point**. 73 | Follow the tutorial to build out your **first AI Agent** step by step! 74 | -------------------------------------------------------------------------------- /agent-starter/README.md: -------------------------------------------------------------------------------- 1 | # 🚀 OpenAI Agent Tutorial – Starter Repo 2 | 3 | This is the **starter code** for my YouTube tutorial: 4 | 👉 _“Your First AI Agent (Next.js + OpenAI Tool Calling)”_ 5 | 6 | We’ll start from a fresh **Next.js 15 project** with: 7 | 8 | - ✅ TypeScript 9 | - ✅ Tailwind CSS 10 | - ✅ App Router 11 | - ✅ `@/*` import alias 12 | 13 | In the tutorial, we’ll add the code that turns this into a working **AI Agent**. 14 | 15 | --- 16 | 17 | ## 📦 Setup 18 | 19 | Clone this repo: 20 | 21 | ```bash 22 | git clone https://github.com/jspruance/ai-agent-tutorial.git 23 | cd openai-agent-tutorial-starter 24 | ``` 25 | 26 | Install dependencies: 27 | 28 | ```bash 29 | npm install 30 | ``` 31 | 32 | Add your OpenAI API key (not yet used, but needed later): 33 | 34 | ```bash 35 | # .env.local 36 | OPENAI_API_KEY=your_api_key_here 37 | ``` 38 | 39 | --- 40 | 41 | ## ▶️ Run the Dev Server 42 | 43 | ```bash 44 | npm run dev 45 | ``` 46 | 47 | Then open [http://localhost:3000](http://localhost:3000) in your browser. 48 | 49 | You should see a **blank Next.js app** with Tailwind installed. 50 | 51 | --- 52 | 53 | ## 🗂️ Project Structure 54 | 55 | ``` 56 | src/ 57 | └── app/ 58 | ├── page.tsx # Simple placeholder page 59 | └── api/ # Agent code will go here in the tutorial 60 | ``` 61 | 62 | --- 63 | 64 | ## 📚 References 65 | 66 | - [Next.js App Router Docs](https://nextjs.org/docs/app) 67 | - [Tailwind CSS Docs](https://tailwindcss.com/docs/guides/nextjs) 68 | - [OpenAI Docs](https://platform.openai.com/docs/guides/function-calling) 69 | 70 | --- 71 | 72 | 👉 This repo is just the **starting point**. 73 | Follow the tutorial to build out your **first AI Agent** step by step! 74 | -------------------------------------------------------------------------------- /agent-starter/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | // app/page.tsx 2 | "use client"; 3 | import { useState } from "react"; 4 | 5 | export default function Home() { 6 | const [query, setQuery] = useState(""); 7 | const [answer, setAnswer] = useState(""); 8 | const [loading, setLoading] = useState(false); 9 | 10 | const askAgent = async () => { 11 | // TODO: Call our /api/agent endpoint 12 | setAnswer("Agent not connected yet"); 13 | }; 14 | 15 | return ( 16 |22 | Ask me math questions, check the weather, or even get a programming 23 | joke! 24 |
25 | 26 |{answer}
45 |29 | Ask me math questions, check the weather, or even get a programming 30 | joke! 31 |
32 | 33 |{answer}
52 |🤖 {answer}
} 158 | ``` 159 | --------------------------------------------------------------------------------