Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:25:09 +08:00
commit 9475095985
30 changed files with 5609 additions and 0 deletions

View File

@@ -0,0 +1,171 @@
/**
* Next.js App Router API Route with OpenAI Agents
*
* File: app/api/agent/route.ts
*
* Demonstrates:
* - Creating API routes with agents
* - Handling POST requests
* - Streaming responses
* - Error handling
*/
import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { Agent, run, tool } from '@openai/agents';
// ========================================
// Agent Definition
// ========================================
const searchTool = tool({
name: 'search',
description: 'Search for information',
parameters: z.object({
query: z.string(),
}),
execute: async ({ query }) => {
// Implement your search logic
return `Search results for: ${query}`;
},
});
const assistantAgent = new Agent({
name: 'Assistant',
instructions: 'You are a helpful assistant. Use the search tool when you need to find information.',
tools: [searchTool],
model: 'gpt-4o-mini',
});
// ========================================
// POST /api/agent
// ========================================
export async function POST(request: NextRequest) {
try {
// Parse request body
const body = await request.json();
const { message, stream = false } = body;
if (!message || typeof message !== 'string') {
return NextResponse.json(
{ error: 'Invalid message' },
{ status: 400 }
);
}
// ========================================
// Non-Streaming Response
// ========================================
if (!stream) {
const result = await run(assistantAgent, message, {
maxTurns: 5,
});
return NextResponse.json({
response: result.finalOutput,
agent: result.currentAgent?.name,
tokens: result.usage.totalTokens,
history: result.history.length,
});
}
// ========================================
// Streaming Response
// ========================================
const streamResult = await run(assistantAgent, message, {
stream: true,
maxTurns: 5,
});
// Create readable stream
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
try {
for await (const event of streamResult) {
if (event.type === 'raw_model_stream_event') {
const chunk = event.data?.choices?.[0]?.delta?.content || '';
if (chunk) {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({ type: 'chunk', content: chunk })}\n\n`)
);
}
} else if (event.type === 'agent_updated_stream_event') {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({
type: 'agent_change',
agent: event.agent.name
})}\n\n`)
);
} else if (event.type === 'run_item_stream_event') {
if (event.name === 'tool_call') {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({
type: 'tool_call',
name: (event.item as any).name,
arguments: (event.item as any).arguments,
})}\n\n`)
);
}
}
}
await streamResult.completed;
// Send completion event
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({
type: 'done',
tokens: streamResult.result.usage.totalTokens
})}\n\n`)
);
controller.close();
} catch (error: any) {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({
type: 'error',
message: error.message
})}\n\n`)
);
controller.close();
}
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
} catch (error: any) {
console.error('API route error:', error);
return NextResponse.json(
{ error: error.message || 'Internal server error' },
{ status: 500 }
);
}
}
// ========================================
// GET /api/agent (Info)
// ========================================
export async function GET() {
return NextResponse.json({
agent: assistantAgent.name,
tools: assistantAgent.tools?.map((t: any) => t.name) || [],
model: assistantAgent.model,
});
}