The APSquared site provides AI-powered tools to help users with various tasks. The site is built using Next.js and acts as a frontend for the agent service. To learn more about the agent service, see the agent-service repository.
Visit APSquared.co to test out the agents.
Read more about how to build full stack AI agents using NextJS and LangGraph in our blog post.
To add a new agent follow these steps (assuming you have deployed the agents):
Create a new file in utils/your-agent-name/types.ts
:
import { AgentState } from "../agentclient/schema/schema";
// Define the input type for your agent
export interface YourAgentInput {
// Add your input fields here
field1: string;
field2: number;
// etc...
}
// Define the state type for your agent
export interface YourAgentState extends AgentState {
// Add your state fields here
results?: any[];
status?: string;
// etc...
}
Create a new file in utils/your-agent-name/your-agent-client.ts
:
import { BaseAgentClient } from "../agentclient/base-agent-client";
import { YourAgentInput, YourAgentState } from "./types";
const YOUR_AGENT = 'your-agent-name';
export class YourAgentClient extends BaseAgentClient {
constructor() {
super(YOUR_AGENT);
}
async startYourAgent(input: YourAgentInput): Promise<YourAgentState> {
const response = await this.startAgent(input);
return response as YourAgentState;
}
async getYourAgentStatus(runId: string): Promise<YourAgentState> {
const response = await this.getStatus(runId);
return response as YourAgentState;
}
}
// Create a singleton instance
export const yourAgentClient = new YourAgentClient();
Create the following files in app/api/your-agent-name/
:
import { NextResponse } from "next/server";
import { yourAgentClient } from "@/utils/your-agent-name/your-agent-client";
export async function POST(request: Request) {
const body = await request.json();
try {
const response = await yourAgentClient.startYourAgent(body);
return NextResponse.json(response);
} catch (error) {
console.error('Error starting agent:', error);
return NextResponse.json({ error: 'Failed to start agent' }, { status: 500 });
}
}
import { NextResponse } from "next/server";
import { yourAgentClient } from "@/utils/your-agent-name/your-agent-client";
export async function GET(
request: Request,
{ params }: { params: { runId: string } }
) {
try {
const response = await yourAgentClient.getYourAgentStatus(params.runId);
return NextResponse.json(response);
} catch (error) {
console.error('Error getting status:', error);
return NextResponse.json({ error: 'Failed to get status' }, { status: 500 });
}
}
Create a new directory in app/tools/your-agent-name/
with the following files:
export default function YourAgentLayout({
children,
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}
"use client";
import AgentPage from '@/app/components/agent/AgentPage';
import { YourAgentState } from "@/utils/your-agent-name/types";
// Add sample searches for your agent
const SAMPLE_SEARCHES = [
{
title: "Sample Search 1",
description: "Description of sample search 1",
formData: {
field1: "value1",
field2: 123
}
},
// Add more sample searches...
];
// Define how to render results
const renderResults = (state: YourAgentState) => {
// Return JSX to display your agent's results
return (
<div>
{/* Your results rendering logic here */}
</div>
);
};
// Define loading state tasks
const renderLoadingState = (currentState: Partial<YourAgentState>) => {
return [
// Return array of loading state descriptions
"Task 1 description",
"Task 2 description",
// etc...
];
};
export default function YourAgent() {
return (
<AgentPage
title="Your Agent Title"
description="Description of what your agent does"
apiEndpoint="/api/your-agent-name"
sampleSearches={SAMPLE_SEARCHES}
>
{{
renderResults,
renderLoadingState
}}
</AgentPage>
);
}
Add your agent to the appropriate navigation menu or tool list in the site's navigation components.
- Ensure your agent is properly registered with the agent service
- Test the start and status endpoints
- Test the frontend interface
- Verify error handling and edge cases
- Follow the existing naming conventions
- Implement proper type checking
- Add error handling at all levels
- Keep the UI components modular and reusable
- Document any special requirements or dependencies
- Add loading states and error states
- Consider adding unit tests for critical functionality