Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions examples/next-js-chatbot-starter-template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
19 changes: 2 additions & 17 deletions examples/next-js-chatbot-starter-template/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
41 changes: 0 additions & 41 deletions examples/next-js-chatbot-starter-template/lib/agent/index.ts
Original file line number Diff line number Diff line change
@@ -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";
4 changes: 3 additions & 1 deletion examples/next-js-chatbot-starter-template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand All @@ -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"
}
}
3 changes: 3 additions & 0 deletions examples/next-js-chatbot-starter-template/voltagent/agents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { chatbotAgent } from "../lib/agent";
export { sharedMemory } from "../lib/agent";
export { chatbotAgent as agent } from "../lib/agent";
3 changes: 3 additions & 0 deletions examples/next-js-chatbot-starter-template/voltagent/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { agent } from "./agents";
export { chatbotAgent } from "./agents";
export { sharedMemory } from "./agents";
10 changes: 10 additions & 0 deletions examples/next-js-chatbot-starter-template/voltagent/server.ts
Original file line number Diff line number Diff line change
@@ -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(),
});
Comment on lines +5 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add error handling and lifecycle management.

The VoltAgent instance is created but not assigned or managed, which can lead to issues:

  1. No error handling: If initialization fails, the process crashes without meaningful error messages
  2. No graceful shutdown: The server won't clean up resources on SIGTERM/SIGINT
  3. No reference to instance: Cannot access or manage the VoltAgent instance after creation
♻️ Add error handling and lifecycle management
-new VoltAgent({
+const voltAgent = new VoltAgent({
   agents: {
     agent,
   },
   server: honoServer(),
 });
+
+// Graceful shutdown
+process.on("SIGTERM", async () => {
+  console.log("SIGTERM received, shutting down gracefully");
+  await voltAgent.shutdown?.();
+  process.exit(0);
+});
+
+process.on("SIGINT", async () => {
+  console.log("SIGINT received, shutting down gracefully");
+  await voltAgent.shutdown?.();
+  process.exit(0);
+});
+
+process.on("unhandledRejection", (reason, promise) => {
+  console.error("Unhandled Rejection at:", promise, "reason:", reason);
+  process.exit(1);
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
new VoltAgent({
agents: {
agent,
},
server: honoServer(),
});
const voltAgent = new VoltAgent({
agents: {
agent,
},
server: honoServer(),
});
// Graceful shutdown
process.on("SIGTERM", async () => {
console.log("SIGTERM received, shutting down gracefully");
await voltAgent.shutdown?.();
process.exit(0);
});
process.on("SIGINT", async () => {
console.log("SIGINT received, shutting down gracefully");
await voltAgent.shutdown?.();
process.exit(0);
});
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection at:", promise, "reason:", reason);
process.exit(1);
});
🤖 Prompt for AI Agents
In @examples/next-js-chatbot-starter-template/voltagent/server.ts around lines 5
- 10, Assign the VoltAgent instance to a variable (e.g., const voltagent = new
VoltAgent(...)) and wrap creation/initialization in a try/catch to log errors
via console.error and exit(1) on failure; after successful creation, add signal
handlers for SIGINT and SIGTERM that call the instance's cleanup method (e.g.,
voltagent.shutdown() or voltagent.close() — check which API exists) and then
process.exit, and ensure any async init (if VoltAgent exposes init/start) is
awaited before registering handlers so the instance reference is valid for
graceful shutdown.

11 changes: 9 additions & 2 deletions examples/with-assistant-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
4 changes: 3 additions & 1 deletion examples/with-assistant-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"
}
}
51 changes: 51 additions & 0 deletions examples/with-assistant-ui/voltagent/agents.ts
Original file line number Diff line number Diff line change
@@ -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;
72 changes: 2 additions & 70 deletions examples/with-assistant-ui/voltagent/index.ts
Original file line number Diff line number Diff line change
@@ -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";
10 changes: 10 additions & 0 deletions examples/with-assistant-ui/voltagent/server.ts
Original file line number Diff line number Diff line change
@@ -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(),
});
10 changes: 9 additions & 1 deletion examples/with-client-side-tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion examples/with-client-side-tools/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { agent } from "@/lib/agent";
import { agent } from "@/voltagent";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Read the route.ts file to see actual code and confirm JSON.stringify usage
echo "=== Reading route.ts file ==="
cat -n examples/with-client-side-tools/app/api/chat/route.ts | head -30

# 2) Check the voltagent module structure in the example
echo -e "\n=== Exploring voltagent module in example ==="
find examples/with-client-side-tools -name "*.ts" -o -name "*.js" | grep -E "(voltagent|index)" | head -20

# 3) Verify safeStringify is available in the codebase
echo -e "\n=== Searching for safeStringify usage ==="
rg -n "safeStringify" --type=ts -B2 -A2 | head -30

Repository: VoltAgent/voltagent

Length of output: 3331


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Reading voltagent/index.ts ==="
cat -n examples/with-client-side-tools/voltagent/index.ts

echo -e "\n=== Reading voltagent/agents.ts (first 30 lines) ==="
head -30 examples/with-client-side-tools/voltagent/agents.ts

echo -e "\n=== Checking for safeStringify import patterns in examples ==="
rg -n "safeStringify" examples/ --type=ts || echo "No safeStringify found in examples"

echo -e "\n=== Checking @voltagent/internal availability in example ==="
fd -a package.json examples/with-client-side-tools | xargs cat | grep -A5 -B5 "@voltagent" || echo "Checking package structure..."

Repository: VoltAgent/voltagent

Length of output: 2851


Replace JSON.stringify with safeStringify from @voltagent/internal

This file violates the TypeScript guideline which requires using safeStringify instead of JSON.stringify. Import it and use it for the error response at lines 16-18:

import { safeStringify } from "@voltagent/internal";

Then replace JSON.stringify({...}) with safeStringify({...}) in the error handler.

🤖 Prompt for AI Agents
In @examples/with-client-side-tools/app/api/chat/route.ts at line 1, The error
response uses JSON.stringify directly; import safeStringify from
"@voltagent/internal" (add: import { safeStringify } from "@voltagent/internal";
alongside the existing import of agent) and replace JSON.stringify({...}) with
safeStringify({...}) in the error handler where the response body is created
(the error response block that currently calls JSON.stringify). Ensure you only
change the import and the serializer call (leave the payload object unchanged).


export async function POST(req: Request) {
try {
Expand Down
5 changes: 4 additions & 1 deletion examples/with-client-side-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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": [
Expand All @@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -44,9 +44,3 @@ export const agent = new Agent({
model: openai("gpt-4o-mini"),
tools: Object.values(tools),
});

new VoltAgent({
agents: {
agent,
},
});
1 change: 1 addition & 0 deletions examples/with-client-side-tools/voltagent/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { agent } from "./agents";
10 changes: 10 additions & 0 deletions examples/with-client-side-tools/voltagent/server.ts
Original file line number Diff line number Diff line change
@@ -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(),
});
Loading