diff --git a/examples/next-js-chatbot-starter-template/README.md b/examples/next-js-chatbot-starter-template/README.md index 32deb02f8..8aab222d7 100644 --- a/examples/next-js-chatbot-starter-template/README.md +++ b/examples/next-js-chatbot-starter-template/README.md @@ -51,3 +51,14 @@ VoltAgent is an open-source TypeScript framework for creating and managing AI ag ```bash npm create voltagent-app@latest -- --example next-js-chatbot-starter-template ``` + +## Run the Example + +Run the Next.js app and the VoltAgent built-in server in separate terminals: + +```bash +pnpm dev +pnpm voltagent:run +``` + +Then open `https://console.voltagent.dev` and connect it to `http://localhost:3141`. diff --git a/examples/next-js-chatbot-starter-template/app/api/chat/route.ts b/examples/next-js-chatbot-starter-template/app/api/chat/route.ts index df176bd30..dcf502bc6 100644 --- a/examples/next-js-chatbot-starter-template/app/api/chat/route.ts +++ b/examples/next-js-chatbot-starter-template/app/api/chat/route.ts @@ -1,6 +1,6 @@ -import { chatbotAgent } from "@/lib/agent"; import { validateAIConfig } from "@/lib/ai/config"; import type { ChatRequest } from "@/lib/types/api"; +import { chatbotAgent } from "@/voltagent"; export async function POST(req: Request) { try { diff --git a/examples/next-js-chatbot-starter-template/instrumentation.ts b/examples/next-js-chatbot-starter-template/instrumentation.ts index e5964280b..bec2e8d40 100644 --- a/examples/next-js-chatbot-starter-template/instrumentation.ts +++ b/examples/next-js-chatbot-starter-template/instrumentation.ts @@ -2,28 +2,13 @@ * Next.js Instrumentation File * * This file is called once when the Next.js server starts up (in both dev and production). - * We use it to initialize the VoltAgent singleton, which starts the VoltOps console - * debugging server on port 3141. + * The VoltAgent built-in server should be started in a separate process. * * @see https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation */ -import type { VoltAgent } from "@voltagent/core"; - -declare global { - var voltAgent: VoltAgent | undefined; -} - export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { - // Initialize VoltAgent singleton on server startup - // This ensures the debugging server (port 3141) starts immediately - const { voltAgent } = await import("./lib/agent"); - - console.log("✓ VoltAgent initialized"); - console.log("✓ VoltOps console available at http://localhost:3141"); - - // Keep reference to prevent garbage collection - globalThis.voltAgent = voltAgent; + return; } } diff --git a/examples/next-js-chatbot-starter-template/lib/agent/index.ts b/examples/next-js-chatbot-starter-template/lib/agent/index.ts index 1d81e9292..6d6e0d6a2 100644 --- a/examples/next-js-chatbot-starter-template/lib/agent/index.ts +++ b/examples/next-js-chatbot-starter-template/lib/agent/index.ts @@ -1,43 +1,2 @@ -/** - * Agent Configuration Exports - * - * Central export point for agent-related configurations. - * Implements singleton pattern for VoltAgent to enable VoltOps console debugging. - */ - -import { VoltAgent } from "@voltagent/core"; -import { honoServer } from "@voltagent/server-hono"; -import { chatbotAgent } from "./agent"; - -// Type declaration for global augmentation -declare global { - var voltAgentInstance: VoltAgent | undefined; -} - -/** - * Singleton VoltAgent instance getter - * - * Creates a single VoltAgent instance with the Hono server for VoltOps console debugging. - * The singleton pattern ensures the debugging port (3141) is properly opened and maintained - * across hot reloads in Next.js development mode. - * - * @returns {VoltAgent} The singleton VoltAgent instance - */ -function getVoltAgentInstance() { - if (!globalThis.voltAgentInstance) { - globalThis.voltAgentInstance = new VoltAgent({ - agents: { - chatbotAgent, - }, - server: honoServer(), - }); - } - return globalThis.voltAgentInstance; -} - -// Initialize the singleton -export const voltAgent = getVoltAgentInstance(); - -// Export individual components export { chatbotAgent } from "./agent"; export { sharedMemory } from "./memory"; diff --git a/examples/next-js-chatbot-starter-template/package.json b/examples/next-js-chatbot-starter-template/package.json index 02fdd0bd2..308cb2ef3 100644 --- a/examples/next-js-chatbot-starter-template/package.json +++ b/examples/next-js-chatbot-starter-template/package.json @@ -57,6 +57,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "tailwindcss": "^4.1.4", + "tsx": "^4.19.3", "tw-animate-css": "^1.4.0", "typescript": "^5.8.2" }, @@ -70,6 +71,7 @@ "build": "next build", "dev": "next dev", "start": "next start", - "volt": "volt" + "volt": "volt", + "voltagent:run": "tsx --env-file=.env ./voltagent/server.ts" } } diff --git a/examples/next-js-chatbot-starter-template/voltagent/agents.ts b/examples/next-js-chatbot-starter-template/voltagent/agents.ts new file mode 100644 index 000000000..ded739932 --- /dev/null +++ b/examples/next-js-chatbot-starter-template/voltagent/agents.ts @@ -0,0 +1,3 @@ +export { chatbotAgent } from "../lib/agent"; +export { sharedMemory } from "../lib/agent"; +export { chatbotAgent as agent } from "../lib/agent"; diff --git a/examples/next-js-chatbot-starter-template/voltagent/index.ts b/examples/next-js-chatbot-starter-template/voltagent/index.ts new file mode 100644 index 000000000..990e9ed76 --- /dev/null +++ b/examples/next-js-chatbot-starter-template/voltagent/index.ts @@ -0,0 +1,3 @@ +export { agent } from "./agents"; +export { chatbotAgent } from "./agents"; +export { sharedMemory } from "./agents"; diff --git a/examples/next-js-chatbot-starter-template/voltagent/server.ts b/examples/next-js-chatbot-starter-template/voltagent/server.ts new file mode 100644 index 000000000..5b5dc1bca --- /dev/null +++ b/examples/next-js-chatbot-starter-template/voltagent/server.ts @@ -0,0 +1,10 @@ +import { VoltAgent } from "@voltagent/core"; +import { honoServer } from "@voltagent/server-hono"; +import { agent } from "./agents"; + +new VoltAgent({ + agents: { + agent, + }, + server: honoServer(), +}); diff --git a/examples/with-assistant-ui/README.md b/examples/with-assistant-ui/README.md index 0c8c90534..37771e5f7 100644 --- a/examples/with-assistant-ui/README.md +++ b/examples/with-assistant-ui/README.md @@ -2,16 +2,23 @@ Assistant UI starter wired to VoltAgent. ## Getting Started -1. Copy `.env.example` to `.env.local` and set: +1. Copy `.env.example` to `.env` and set: - `OPENAI_API_KEY` 2. Start the dev server: ```bash pnpm dev ``` +3. Start the VoltAgent built-in server in a separate terminal: + ```bash + pnpm voltagent:run + ``` + +Then open `https://console.voltagent.dev` and connect it to `http://localhost:3141`. ## How it works -- `voltagent/index.ts` defines the `AssistantUIAgent` using VoltAgent with shared LibSQL-backed memory (`voltagent/memory.ts`). +- `voltagent/agents.ts` defines the `AssistantUIAgent` with shared LibSQL-backed memory (`voltagent/memory.ts`). +- `voltagent/server.ts` starts the built-in server that exposes the REST API used by `console.voltagent.dev`. - The agent includes a `getWeather` tool (mock data) to demonstrate tool calls from the UI. - `app/api/chat/route.ts` streams responses from the VoltAgent agent using `toUIMessageStreamResponse`, so Assistant UI receives tool/reasoning aware events. - `app/assistant.tsx` uses `AssistantChatTransport` pointing to `/api/chat` to keep the UI runtime in sync with the agent. diff --git a/examples/with-assistant-ui/package.json b/examples/with-assistant-ui/package.json index aab16a961..9d5343016 100644 --- a/examples/with-assistant-ui/package.json +++ b/examples/with-assistant-ui/package.json @@ -37,6 +37,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "tailwindcss": "^4.1.4", + "tsx": "^4.19.3", "typescript": "^5.8.2" }, "private": true, @@ -48,6 +49,7 @@ "scripts": { "build": "next build", "dev": "next dev --turbopack", - "start": "next start" + "start": "next start", + "voltagent:run": "tsx --env-file=.env ./voltagent/server.ts" } } diff --git a/examples/with-assistant-ui/voltagent/agents.ts b/examples/with-assistant-ui/voltagent/agents.ts new file mode 100644 index 000000000..b32af78a3 --- /dev/null +++ b/examples/with-assistant-ui/voltagent/agents.ts @@ -0,0 +1,51 @@ +import { openai } from "@ai-sdk/openai"; +import { Agent, createTool } from "@voltagent/core"; +import { z } from "zod"; +import { sharedMemory } from "./memory"; + +const weatherOutputSchema = z.object({ + weather: z.object({ + location: z.string(), + temperature: z.number(), + condition: z.string(), + humidity: z.number(), + windSpeed: z.number(), + }), + message: z.string(), +}); + +const weatherTool = createTool({ + name: "getWeather", + description: "Get the current weather for a specific location", + parameters: z.object({ + location: z.string().describe("The city or location to get weather for"), + }), + outputSchema: weatherOutputSchema, + execute: async ({ location }) => { + const mockWeatherData = { + location, + temperature: Math.floor(Math.random() * 30) + 5, + condition: ["Sunny", "Cloudy", "Rainy", "Snowy", "Partly Cloudy"][ + Math.floor(Math.random() * 5) + ], + humidity: Math.floor(Math.random() * 60) + 30, + windSpeed: Math.floor(Math.random() * 30), + }; + + return { + weather: mockWeatherData, + message: `Current weather in ${location}: ${mockWeatherData.temperature}°C and ${mockWeatherData.condition.toLowerCase()} with ${mockWeatherData.humidity}% humidity and wind speed of ${mockWeatherData.windSpeed} km/h.`, + }; + }, +}); + +export const assistantAgent = new Agent({ + name: "AssistantUIAgent", + instructions: + "You are a helpful AI that keeps responses concise, explains reasoning when useful, can gracefully describe any image or file attachments the user provides, and can call the getWeather tool for weather questions. Ask clarifying questions when context is missing.", + model: openai("gpt-4o-mini"), + tools: [weatherTool], + memory: sharedMemory, +}); + +export const agent = assistantAgent; diff --git a/examples/with-assistant-ui/voltagent/index.ts b/examples/with-assistant-ui/voltagent/index.ts index 1d96a4e4d..be3ce3c1a 100644 --- a/examples/with-assistant-ui/voltagent/index.ts +++ b/examples/with-assistant-ui/voltagent/index.ts @@ -1,70 +1,2 @@ -import { openai } from "@ai-sdk/openai"; -import { Agent, VoltAgent, createTool } from "@voltagent/core"; -import { honoServer } from "@voltagent/server-hono"; -import { z } from "zod"; -import { sharedMemory } from "./memory"; - -const weatherOutputSchema = z.object({ - weather: z.object({ - location: z.string(), - temperature: z.number(), - condition: z.string(), - humidity: z.number(), - windSpeed: z.number(), - }), - message: z.string(), -}); - -const weatherTool = createTool({ - name: "getWeather", - description: "Get the current weather for a specific location", - parameters: z.object({ - location: z.string().describe("The city or location to get weather for"), - }), - outputSchema: weatherOutputSchema, - execute: async ({ location }) => { - const mockWeatherData = { - location, - temperature: Math.floor(Math.random() * 30) + 5, - condition: ["Sunny", "Cloudy", "Rainy", "Snowy", "Partly Cloudy"][ - Math.floor(Math.random() * 5) - ], - humidity: Math.floor(Math.random() * 60) + 30, - windSpeed: Math.floor(Math.random() * 30), - }; - - return { - weather: mockWeatherData, - message: `Current weather in ${location}: ${mockWeatherData.temperature}°C and ${mockWeatherData.condition.toLowerCase()} with ${mockWeatherData.humidity}% humidity and wind speed of ${mockWeatherData.windSpeed} km/h.`, - }; - }, -}); - -const assistantAgent = new Agent({ - name: "AssistantUIAgent", - instructions: - "You are a helpful AI that keeps responses concise, explains reasoning when useful, can gracefully describe any image or file attachments the user provides, and can call the getWeather tool for weather questions. Ask clarifying questions when context is missing.", - model: openai("gpt-4o-mini"), - tools: [weatherTool], - memory: sharedMemory, -}); - -declare global { - // eslint-disable-next-line no-var - var voltAssistant: VoltAgent | undefined; -} - -function getVoltAgentInstance() { - if (!globalThis.voltAssistant) { - globalThis.voltAssistant = new VoltAgent({ - agents: { - assistantAgent, - }, - server: honoServer(), - }); - } - return globalThis.voltAssistant; -} - -export const voltAgent = getVoltAgentInstance(); -export const agent = assistantAgent; +export { agent } from "./agents"; +export { assistantAgent } from "./agents"; diff --git a/examples/with-assistant-ui/voltagent/server.ts b/examples/with-assistant-ui/voltagent/server.ts new file mode 100644 index 000000000..5b5dc1bca --- /dev/null +++ b/examples/with-assistant-ui/voltagent/server.ts @@ -0,0 +1,10 @@ +import { VoltAgent } from "@voltagent/core"; +import { honoServer } from "@voltagent/server-hono"; +import { agent } from "./agents"; + +new VoltAgent({ + agents: { + agent, + }, + server: honoServer(), +}); diff --git a/examples/with-client-side-tools/README.md b/examples/with-client-side-tools/README.md index 7b75dac58..bf09c9124 100644 --- a/examples/with-client-side-tools/README.md +++ b/examples/with-client-side-tools/README.md @@ -30,7 +30,15 @@ pnpm install pnpm dev ``` -4. Open [http://localhost:3000](http://localhost:3000) +4. Start the VoltAgent built-in server in a separate terminal: + +```bash +pnpm voltagent:run +``` + +5. Open [http://localhost:3000](http://localhost:3000) + +To inspect runs, open `https://console.voltagent.dev` and connect it to `http://localhost:3141`. ### How It Works diff --git a/examples/with-client-side-tools/app/api/chat/route.ts b/examples/with-client-side-tools/app/api/chat/route.ts index 216697224..b15d90b3f 100644 --- a/examples/with-client-side-tools/app/api/chat/route.ts +++ b/examples/with-client-side-tools/app/api/chat/route.ts @@ -1,4 +1,4 @@ -import { agent } from "@/lib/agent"; +import { agent } from "@/voltagent"; export async function POST(req: Request) { try { diff --git a/examples/with-client-side-tools/package.json b/examples/with-client-side-tools/package.json index 4d35774eb..9d2e7ca97 100644 --- a/examples/with-client-side-tools/package.json +++ b/examples/with-client-side-tools/package.json @@ -6,6 +6,7 @@ "@ai-sdk/react": "^3.0.0", "@libsql/client": "^0.15.0", "@voltagent/core": "^2.0.9", + "@voltagent/server-hono": "^2.0.3", "@voltagent/vercel-ai": "^1.0.0", "@voltagent/vercel-ui": "^1.0.1", "ai": "^6.0.0", @@ -18,6 +19,7 @@ "@types/node": "^24.2.1", "@types/react": "^19", "@types/react-dom": "^19", + "tsx": "^4.19.3", "typescript": "^5.8.2" }, "keywords": [ @@ -36,6 +38,7 @@ "scripts": { "build": "next build", "dev": "next dev", - "start": "next start" + "start": "next start", + "voltagent:run": "tsx --env-file=.env ./voltagent/server.ts" } } diff --git a/examples/with-client-side-tools/lib/agent.ts b/examples/with-client-side-tools/voltagent/agents.ts similarity index 91% rename from examples/with-client-side-tools/lib/agent.ts rename to examples/with-client-side-tools/voltagent/agents.ts index 0421d277f..190064073 100644 --- a/examples/with-client-side-tools/lib/agent.ts +++ b/examples/with-client-side-tools/voltagent/agents.ts @@ -1,5 +1,5 @@ import { openai } from "@ai-sdk/openai"; -import VoltAgent, { Agent, createTool } from "@voltagent/core"; +import { Agent, createTool } from "@voltagent/core"; import { z } from "zod"; // Tools definitions - those without execute are automatically client-side @@ -44,9 +44,3 @@ export const agent = new Agent({ model: openai("gpt-4o-mini"), tools: Object.values(tools), }); - -new VoltAgent({ - agents: { - agent, - }, -}); diff --git a/examples/with-client-side-tools/voltagent/index.ts b/examples/with-client-side-tools/voltagent/index.ts new file mode 100644 index 000000000..735f34d9d --- /dev/null +++ b/examples/with-client-side-tools/voltagent/index.ts @@ -0,0 +1 @@ +export { agent } from "./agents"; diff --git a/examples/with-client-side-tools/voltagent/server.ts b/examples/with-client-side-tools/voltagent/server.ts new file mode 100644 index 000000000..5b5dc1bca --- /dev/null +++ b/examples/with-client-side-tools/voltagent/server.ts @@ -0,0 +1,10 @@ +import { VoltAgent } from "@voltagent/core"; +import { honoServer } from "@voltagent/server-hono"; +import { agent } from "./agents"; + +new VoltAgent({ + agents: { + agent, + }, + server: honoServer(), +}); diff --git a/examples/with-nextjs-resumable-stream/README.md b/examples/with-nextjs-resumable-stream/README.md index 5de42ba0f..0cd18511d 100644 --- a/examples/with-nextjs-resumable-stream/README.md +++ b/examples/with-nextjs-resumable-stream/README.md @@ -51,3 +51,14 @@ VoltAgent is an open-source TypeScript framework for creating and managing AI ag ```bash npm create voltagent-app@latest -- --example with-nextjs-resumable-stream ``` + +## Run the Example + +Run the Next.js app and the VoltAgent built-in server in separate terminals: + +```bash +pnpm dev +pnpm voltagent:run +``` + +Then open `https://console.voltagent.dev` and connect it to `http://localhost:3141`. diff --git a/examples/with-nextjs-resumable-stream/package.json b/examples/with-nextjs-resumable-stream/package.json index 17956ecb8..0d218a525 100644 --- a/examples/with-nextjs-resumable-stream/package.json +++ b/examples/with-nextjs-resumable-stream/package.json @@ -53,6 +53,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "tailwindcss": "^4.1.4", + "tsx": "^4.19.3", "tw-animate-css": "^1.4.0", "typescript": "^5.8.2" }, @@ -66,6 +67,7 @@ "build": "next build", "dev": "next dev", "start": "next start", - "volt": "volt" + "volt": "volt", + "voltagent:run": "tsx --env-file=.env ./voltagent/server.ts" } } diff --git a/examples/with-nextjs-resumable-stream/voltagent/agents.ts b/examples/with-nextjs-resumable-stream/voltagent/agents.ts new file mode 100644 index 000000000..5533be727 --- /dev/null +++ b/examples/with-nextjs-resumable-stream/voltagent/agents.ts @@ -0,0 +1,33 @@ +import { openai } from "@ai-sdk/openai"; +import { NodeFilesystemBackend, PlanAgent } from "@voltagent/core"; +import { sharedMemory } from "./memory"; +import { internetSearch } from "./tools"; + +const researchInstructions = [ + "You are an expert researcher. Your job is to conduct thorough research and then write a polished report.", + "", + "You have access to an internet search tool as your primary means of gathering information.", + "", + "## internet_search", + "Use this to run an internet search for a given query. You can specify the max number of results to return, the topic, and whether raw content should be included.", +].join("\n"); + +export const agent = new PlanAgent({ + name: "deep-research-agent", + systemPrompt: researchInstructions, + model: openai("gpt-4o-mini"), + tools: [internetSearch], + memory: sharedMemory, + maxSteps: 100, + summarization: { + triggerTokens: 1200, + keepMessages: 6, + maxOutputTokens: 600, + }, + filesystem: { + backend: new NodeFilesystemBackend({ + rootDir: process.cwd(), + virtualMode: true, + }), + }, +}); diff --git a/examples/with-nextjs-resumable-stream/voltagent/index.ts b/examples/with-nextjs-resumable-stream/voltagent/index.ts index 7c2474e72..735f34d9d 100644 --- a/examples/with-nextjs-resumable-stream/voltagent/index.ts +++ b/examples/with-nextjs-resumable-stream/voltagent/index.ts @@ -1,55 +1 @@ -import { openai } from "@ai-sdk/openai"; -import { NodeFilesystemBackend, PlanAgent, VoltAgent, createTool } from "@voltagent/core"; -import { honoServer } from "@voltagent/server-hono"; -import { sharedMemory } from "./memory"; -import { internetSearch } from "./tools"; - -const researchInstructions = [ - "You are an expert researcher. Your job is to conduct thorough research and then write a polished report.", - "", - "You have access to an internet search tool as your primary means of gathering information.", - "", - "## internet_search", - "Use this to run an internet search for a given query. You can specify the max number of results to return, the topic, and whether raw content should be included.", -].join("\n"); - -export const agent = new PlanAgent({ - name: "deep-research-agent", - systemPrompt: researchInstructions, - model: openai("gpt-4o-mini"), - tools: [internetSearch], - memory: sharedMemory, - maxSteps: 100, - summarization: { - triggerTokens: 1200, - keepMessages: 6, - maxOutputTokens: 600, - }, - filesystem: { - backend: new NodeFilesystemBackend({ - rootDir: process.cwd(), - virtualMode: true, - }), - }, -}); - -// Type declaration for global augmentation -declare global { - var voltAgentInstance: VoltAgent | undefined; -} - -// Singleton initialization function -function getVoltAgentInstance() { - if (!globalThis.voltAgentInstance) { - globalThis.voltAgentInstance = new VoltAgent({ - agents: { - agent, - }, - server: honoServer(), - }); - } - return globalThis.voltAgentInstance; -} - -// Initialize the singleton -export const voltAgent = getVoltAgentInstance(); +export { agent } from "./agents"; diff --git a/examples/with-nextjs-resumable-stream/voltagent/server.ts b/examples/with-nextjs-resumable-stream/voltagent/server.ts new file mode 100644 index 000000000..5b5dc1bca --- /dev/null +++ b/examples/with-nextjs-resumable-stream/voltagent/server.ts @@ -0,0 +1,10 @@ +import { VoltAgent } from "@voltagent/core"; +import { honoServer } from "@voltagent/server-hono"; +import { agent } from "./agents"; + +new VoltAgent({ + agents: { + agent, + }, + server: honoServer(), +}); diff --git a/examples/with-nextjs/README.md b/examples/with-nextjs/README.md index 58c3aaf32..53b1fbe4b 100644 --- a/examples/with-nextjs/README.md +++ b/examples/with-nextjs/README.md @@ -51,3 +51,14 @@ VoltAgent is an open-source TypeScript framework for creating and managing AI ag ```bash npm create voltagent-app@latest -- --example with-nextjs ``` + +## VoltAgent Built-in Server + +Run the Next.js app and the built-in server in separate terminals: + +```bash +pnpm dev +pnpm voltagent:run +``` + +Then open `https://console.voltagent.dev` and connect it to `http://localhost:3141`. diff --git a/examples/with-nextjs/app/api/chat/route.ts b/examples/with-nextjs/app/api/chat/route.ts index d4906539b..3259b7a3a 100644 --- a/examples/with-nextjs/app/api/chat/route.ts +++ b/examples/with-nextjs/app/api/chat/route.ts @@ -1,4 +1,4 @@ -import { supervisorAgent, voltAgent } from "@/voltagent"; +import { supervisorAgent } from "@/voltagent"; import { setWaitUntil } from "@voltagent/core"; import { after } from "next/server"; diff --git a/examples/with-nextjs/app/api/health/route.ts b/examples/with-nextjs/app/api/health/route.ts new file mode 100644 index 000000000..bed4f46b8 --- /dev/null +++ b/examples/with-nextjs/app/api/health/route.ts @@ -0,0 +1,5 @@ +import { NextResponse } from "next/server"; + +export async function GET() { + return NextResponse.json({ message: "OK" }); +} diff --git a/examples/with-nextjs/package.json b/examples/with-nextjs/package.json index 9d5ce9ef8..80f424dfd 100644 --- a/examples/with-nextjs/package.json +++ b/examples/with-nextjs/package.json @@ -26,6 +26,7 @@ "@types/node": "^24.2.1", "@types/react": "^19", "@types/react-dom": "^19", + "tsx": "^4.19.3", "typescript": "^5.8.2" }, "private": true, @@ -39,6 +40,7 @@ "dev": "next dev --turbopack", "lint": "next lint", "start": "next start", - "volt": "volt" + "volt": "volt", + "voltagent:run": "tsx --env-file=.env ./voltagent/server.ts" } } diff --git a/examples/with-nextjs/voltagent/agents.ts b/examples/with-nextjs/voltagent/agents.ts new file mode 100644 index 000000000..9eed616a9 --- /dev/null +++ b/examples/with-nextjs/voltagent/agents.ts @@ -0,0 +1,85 @@ +import { openai } from "@ai-sdk/openai"; +import { Agent, createTool } from "@voltagent/core"; +import { z } from "zod"; +import { sharedMemory } from "./memory"; + +// Uppercase conversion tool +const uppercaseTool = createTool({ + name: "uppercase", + description: "Convert text to uppercase", + parameters: z.object({ + text: z.string().describe("Text to convert to uppercase"), + }), + execute: async (args) => { + return { result: args.text.toUpperCase() }; + }, +}); + +// Word count tool +const wordCountTool = createTool({ + name: "countWords", + description: "Count words in text", + parameters: z.object({ + text: z.string().describe("Text to count words in"), + }), + execute: async (args) => { + const words = args.text + .trim() + .split(/\s+/) + .filter((word) => word.length > 0); + return { count: words.length, words: words }; + }, +}); + +// Story writing tool +const storyWriterTool = createTool({ + name: "writeStory", + description: "Write a 50-word story about the given text", + parameters: z.object({ + text: z.string().describe("Text to write a story about"), + }), + execute: async (args) => { + // The agent will handle the creative writing + return { topic: args.text }; + }, +}); + +// Uppercase agent +export const uppercaseAgent = new Agent({ + name: "UppercaseAgent", + instructions: + "You are a text transformer. When given text, use the uppercase tool to convert it to uppercase and return the result.", + model: openai("gpt-4o-mini"), + tools: [uppercaseTool], + memory: sharedMemory, +}); + +// Word count agent +export const wordCountAgent = new Agent({ + name: "WordCountAgent", + instructions: + "You are a text analyzer. When given text, use the countWords tool to count the words and return the count.", + model: openai("gpt-4o-mini"), + tools: [wordCountTool], + memory: sharedMemory, +}); + +// Story writer agent +export const storyWriterAgent = new Agent({ + name: "StoryWriterAgent", + instructions: + "You are a creative story writer. When given text, use the writeStory tool to acknowledge the topic, then write EXACTLY a 50-word story about or inspired by that text. Be creative and engaging. Make sure your story is exactly 50 words, no more, no less.", + model: openai("gpt-4o-mini"), + tools: [storyWriterTool], + memory: sharedMemory, +}); + +// Supervisor agent that delegates to sub-agents +export const supervisorAgent = new Agent({ + name: "Supervisor", + instructions: + "You are a text processing supervisor. When given any text input, you MUST delegate to ALL THREE agents: UppercaseAgent, WordCountAgent, AND StoryWriterAgent. Delegate to all of them to process the text in parallel. Then combine and present all three results to the user: the uppercase version, the word count, and the 50-word story.", + model: openai("gpt-4o-mini"), + subAgents: [uppercaseAgent, wordCountAgent, storyWriterAgent], + memory: sharedMemory, +}); diff --git a/examples/with-nextjs/voltagent/index.ts b/examples/with-nextjs/voltagent/index.ts index 5b681bd7e..2aeb8fe62 100644 --- a/examples/with-nextjs/voltagent/index.ts +++ b/examples/with-nextjs/voltagent/index.ts @@ -1,112 +1,9 @@ -import { openai } from "@ai-sdk/openai"; -import { Agent, VoltAgent, createTool } from "@voltagent/core"; -import { honoServer } from "@voltagent/server-hono"; -import { z } from "zod"; -import { sharedMemory } from "./memory"; -// Uppercase conversion tool -const uppercaseTool = createTool({ - name: "uppercase", - description: "Convert text to uppercase", - parameters: z.object({ - text: z.string().describe("Text to convert to uppercase"), - }), - execute: async (args) => { - return { result: args.text.toUpperCase() }; - }, -}); - -// Word count tool -const wordCountTool = createTool({ - name: "countWords", - description: "Count words in text", - parameters: z.object({ - text: z.string().describe("Text to count words in"), - }), - execute: async (args) => { - const words = args.text - .trim() - .split(/\s+/) - .filter((word) => word.length > 0); - return { count: words.length, words: words }; - }, -}); - -// Story writing tool -const storyWriterTool = createTool({ - name: "writeStory", - description: "Write a 50-word story about the given text", - parameters: z.object({ - text: z.string().describe("Text to write a story about"), - }), - execute: async (args) => { - // The agent will handle the creative writing - return { topic: args.text }; - }, -}); - -// Uppercase agent -const uppercaseAgent = new Agent({ - name: "UppercaseAgent", - instructions: - "You are a text transformer. When given text, use the uppercase tool to convert it to uppercase and return the result.", - model: openai("gpt-4o-mini"), - tools: [uppercaseTool], - memory: sharedMemory, -}); - -// Word count agent -const wordCountAgent = new Agent({ - name: "WordCountAgent", - instructions: - "You are a text analyzer. When given text, use the countWords tool to count the words and return the count.", - model: openai("gpt-4o-mini"), - tools: [wordCountTool], - memory: sharedMemory, -}); - -// Story writer agent -const storyWriterAgent = new Agent({ - name: "StoryWriterAgent", - instructions: - "You are a creative story writer. When given text, use the writeStory tool to acknowledge the topic, then write EXACTLY a 50-word story about or inspired by that text. Be creative and engaging. Make sure your story is exactly 50 words, no more, no less.", - model: openai("gpt-4o-mini"), - tools: [storyWriterTool], - memory: sharedMemory, -}); - -// Supervisor agent that delegates to sub-agents -export const supervisorAgent = new Agent({ - name: "Supervisor", - instructions: - "You are a text processing supervisor. When given any text input, you MUST delegate to ALL THREE agents: UppercaseAgent, WordCountAgent, AND StoryWriterAgent. Delegate to all of them to process the text in parallel. Then combine and present all three results to the user: the uppercase version, the word count, and the 50-word story.", - model: openai("gpt-4o-mini"), - subAgents: [uppercaseAgent, wordCountAgent, storyWriterAgent], - memory: sharedMemory, -}); - -// Type declaration for global augmentation -declare global { - var voltAgentInstance: VoltAgent | undefined; -} - -// Singleton initialization function -function getVoltAgentInstance() { - if (!globalThis.voltAgentInstance) { - globalThis.voltAgentInstance = new VoltAgent({ - agents: { - supervisorAgent, - storyWriterAgent, - wordCountAgent, - uppercaseAgent, - }, - server: honoServer(), - }); - } - return globalThis.voltAgentInstance; -} - -// Initialize the singleton -export const voltAgent = getVoltAgentInstance(); +export { + supervisorAgent, + uppercaseAgent, + wordCountAgent, + storyWriterAgent, +} from "./agents"; // Export the supervisor as the main agent -export const agent = supervisorAgent; +export { supervisorAgent as agent } from "./agents"; diff --git a/examples/with-nextjs/voltagent/server.ts b/examples/with-nextjs/voltagent/server.ts new file mode 100644 index 000000000..07121d6f9 --- /dev/null +++ b/examples/with-nextjs/voltagent/server.ts @@ -0,0 +1,13 @@ +import { VoltAgent } from "@voltagent/core"; +import { honoServer } from "@voltagent/server-hono"; +import { storyWriterAgent, supervisorAgent, uppercaseAgent, wordCountAgent } from "./agents"; + +new VoltAgent({ + agents: { + supervisorAgent, + storyWriterAgent, + wordCountAgent, + uppercaseAgent, + }, + server: honoServer(), +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c6826d50..ad8c4e7c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -387,6 +387,9 @@ importers: tailwindcss: specifier: ^4.1.4 version: 4.1.14 + tsx: + specifier: ^4.19.3 + version: 4.20.4 tw-animate-css: specifier: ^1.4.0 version: 1.4.0 @@ -739,6 +742,9 @@ importers: tailwindcss: specifier: ^4.1.4 version: 4.1.14 + tsx: + specifier: ^4.19.3 + version: 4.20.4 typescript: specifier: ^5.8.2 version: 5.9.2 @@ -883,6 +889,9 @@ importers: '@voltagent/core': specifier: ^2.0.9 version: link:../../packages/core + '@voltagent/server-hono': + specifier: ^2.0.3 + version: link:../../packages/server-hono '@voltagent/vercel-ai': specifier: ^1.0.0 version: 1.0.0(@voltagent/core@packages+core)(zod@3.25.76) @@ -914,6 +923,9 @@ importers: '@types/react-dom': specifier: ^19 version: 19.1.7(@types/react@19.1.10) + tsx: + specifier: ^4.19.3 + version: 4.20.4 typescript: specifier: ^5.8.2 version: 5.9.2 @@ -1878,6 +1890,9 @@ importers: '@types/react-dom': specifier: ^19 version: 19.1.7(@types/react@19.1.10) + tsx: + specifier: ^4.19.3 + version: 4.20.4 typescript: specifier: ^5.8.2 version: 5.9.2 @@ -2032,6 +2047,9 @@ importers: tailwindcss: specifier: ^4.1.4 version: 4.1.14 + tsx: + specifier: ^4.19.3 + version: 4.20.4 tw-animate-css: specifier: ^1.4.0 version: 1.4.0 diff --git a/website/docs/integrations/nextjs.md b/website/docs/integrations/nextjs.md index 9f3498318..b7d55b793 100644 --- a/website/docs/integrations/nextjs.md +++ b/website/docs/integrations/nextjs.md @@ -42,6 +42,16 @@ npm install @voltagent/core @ai-sdk/openai @ai-sdk/react ai zod@^3.25.76 - `ai`: Core AI SDK library for streaming and chat functionality. - `zod`: Used when working with structured outputs. +### VoltAgent Built-in Server Dependencies (Required for Debugging) + +Install the VoltAgent built-in server dependencies (local REST API on `http://localhost:3141`) so you can +debug and inspect agent runs during development through `https://console.voltagent.dev`: + +```bash +npm install @voltagent/server-hono +npm install -D tsx +``` + ## Configure `next.config.ts` Next.js might try to bundle server-side packages by default. To prevent issues with certain packages, you need to mark them as external in your `next.config.ts` file: @@ -71,15 +81,16 @@ OPENAI_API_KEY="your-openai-api-key-here" # Add other environment variables if needed ``` -## Create API Route +If you run the built-in server as a separate process, make sure it loads the same env file +(see the server script below). -Create the main chat API route with the agent and singleton defined inline in `app/api/chat/route.ts`: +## Define the Agent -```typescript title="app/api/chat/route.ts" -import { after } from "next/server"; +Create the agent in `voltagent/agents.ts`: + +```typescript title="voltagent/agents.ts" import { openai } from "@ai-sdk/openai"; -import { Agent, VoltAgent, createTool, setWaitUntil } from "@voltagent/core"; -import { honoServer } from "@voltagent/server-hono"; +import { Agent, createTool } from "@voltagent/core"; import { z } from "zod"; // Simple calculator tool @@ -102,7 +113,6 @@ const calculatorTool = createTool({ }, }); -// Main agent export const agent = new Agent({ name: "CalculatorAgent", instructions: @@ -110,36 +120,34 @@ export const agent = new Agent({ model: openai("gpt-4o-mini"), tools: [calculatorTool], }); +``` -// VoltAgent singleton (augments global scope during dev to avoid re-instantiation) -declare global { - var voltAgentInstance: VoltAgent | undefined; -} +Re-export it from `voltagent/index.ts`: -function getVoltAgentInstance() { - if (!globalThis.voltAgentInstance) { - globalThis.voltAgentInstance = new VoltAgent({ - agents: { - agent, - }, - server: honoServer(), - }); - } - return globalThis.voltAgentInstance; -} +```typescript title="voltagent/index.ts" +export { agent } from "./agents"; +``` -export const voltAgent = getVoltAgentInstance(); +## Create API Route + +Create the main chat API route using only the agent. Avoid instantiating `VoltAgent` or +`honoServer` inside Next.js handlers to prevent port conflicts and `/api` routing issues: + +```typescript title="app/api/chat/route.ts" +import { after } from "next/server"; +import { setWaitUntil } from "@voltagent/core"; +import { agent } from "@/voltagent"; export async function POST(req: Request) { try { const { messages } = await req.json(); const lastMessage = messages[messages.length - 1]; - const result = await agent.streamText([lastMessage]); - // Enable non-blocking OTel export for Vercel/serverless setWaitUntil(after); + const result = await agent.streamText([lastMessage]); + return result.toUIMessageStreamResponse(); } catch (error) { return Response.json({ error: "Internal server error" }, { status: 500 }); @@ -147,6 +155,38 @@ export async function POST(req: Request) { } ``` +## Run the VoltAgent Built-in Server (Separate Process) + +Next.js dev mode can spin up multiple workers. To avoid port conflicts and process exits, +run the built-in server in a separate process. It exposes the REST API that the console uses. + +Create `voltagent/server.ts`: + +```typescript title="voltagent/server.ts" +import { VoltAgent } from "@voltagent/core"; +import { honoServer } from "@voltagent/server-hono"; +import { agent } from "./agents"; + +new VoltAgent({ + agents: { + agent, + }, + server: honoServer({ port: 3141 }), +}); +``` + +Add a script to `package.json`: + +```json title="package.json" +{ + "scripts": { + "voltagent:run": "tsx --env-file=.env.local ./voltagent/server.ts" + } +} +``` + +If you use a different env file, update the `--env-file` flag accordingly. + ## Build the Chat UI Component (Client Component) Create a client component in `app/components/chat.tsx`: @@ -275,6 +315,14 @@ Now you can run your Next.js development server: npm run dev ``` +Start the built-in server in a separate terminal: + +```bash +npm run voltagent:run +``` + +Then open `https://console.voltagent.dev` and connect it to `http://localhost:3141`. + This creates a simple but powerful VoltAgent application with: - **Single agent** with a custom calculator tool diff --git a/website/docs/ui/assistant-ui.md b/website/docs/ui/assistant-ui.md index 2296e857e..fa32be878 100644 --- a/website/docs/ui/assistant-ui.md +++ b/website/docs/ui/assistant-ui.md @@ -33,6 +33,7 @@ npx assistant-ui@latest create ```bash # Server (VoltAgent) pnpm add @voltagent/core @voltagent/libsql @voltagent/server-hono @ai-sdk/openai ai zod +pnpm add -D tsx # Client (Assistant UI template already has these) pnpm add @assistant-ui/react @assistant-ui/react-ai-sdk @assistant-ui/react-markdown @@ -40,12 +41,11 @@ pnpm add @assistant-ui/react @assistant-ui/react-ai-sdk @assistant-ui/react-mark ## Define your VoltAgent -Create `voltagent/index.ts` and wire an agent with shared memory. You can keep it simple—no tools needed to get streaming working. +Create `voltagent/agents.ts` and wire an agent with shared memory. You can keep it simple—no tools needed to get streaming working. -```ts title="examples/with-assistant-ui/voltagent/index.ts" +```ts title="examples/with-assistant-ui/voltagent/agents.ts" import { openai } from "@ai-sdk/openai"; -import { Agent, VoltAgent, createTool } from "@voltagent/core"; -import { honoServer } from "@voltagent/server-hono"; +import { Agent, createTool } from "@voltagent/core"; import { z } from "zod"; import { sharedMemory } from "./memory"; @@ -85,7 +85,7 @@ const weatherTool = createTool({ }, }); -const assistantAgent = new Agent({ +export const assistantAgent = new Agent({ name: "AssistantUIAgent", instructions: "You are a helpful AI that keeps responses concise, explains reasoning when useful, can describe attachments, and can call the getWeather tool for weather questions.", @@ -94,25 +94,16 @@ const assistantAgent = new Agent({ memory: sharedMemory, }); +export const agent = assistantAgent; +``` + > The weather tool uses mocked data for demo purposes—swap in a real API call if needed. -declare global { - // eslint-disable-next-line no-var - var voltAssistant: VoltAgent | undefined; -} +Re-export the agent from `voltagent/index.ts`: -function getVoltAgentInstance() { - if (!globalThis.voltAssistant) { - globalThis.voltAssistant = new VoltAgent({ - agents: { assistantAgent }, - server: honoServer(), - }); - } - return globalThis.voltAssistant; -} - -export const voltAgent = getVoltAgentInstance(); -export const agent = assistantAgent; +```ts title="examples/with-assistant-ui/voltagent/index.ts" +export { agent } from "./agents"; +export { assistantAgent } from "./agents"; ``` Back the memory with LibSQL (Turso/local SQLite) so threads persist: @@ -126,6 +117,31 @@ export const sharedMemory = new Memory({ }); ``` +## Run the VoltAgent built-in server (separate process) + +Create `voltagent/server.ts`: + +```ts title="examples/with-assistant-ui/voltagent/server.ts" +import { VoltAgent } from "@voltagent/core"; +import { honoServer } from "@voltagent/server-hono"; +import { agent } from "./agents"; + +new VoltAgent({ + agents: { agent }, + server: honoServer(), +}); +``` + +Add a script to `package.json`: + +```json title="package.json" +{ + "scripts": { + "voltagent:run": "tsx --env-file=.env ./voltagent/server.ts" + } +} +``` + ## Expose a chat route that streams UI messages Replace the default AI SDK route with VoltAgent. The important part is using `agent.streamText(...).toUIMessageStreamResponse()` so Assistant UI receives tool/reasoning-aware events. @@ -197,6 +213,9 @@ export const Assistant = () => { ```bash pnpm dev +pnpm voltagent:run ``` Open http://localhost:3000 and chat—the UI will stream reasoning and messages from your VoltAgent agent. Attachments and thread persistence are handled automatically via the VoltAgent memory adapter. + +For debugging, open `https://console.voltagent.dev` and connect it to `http://localhost:3141`.