import { streamText, convertToModelMessages, stepCountIs, pruneMessages, } from 'ai'; import { getChatModel } from '@/services/chat/model'; import { getChatInstructions } from '@/services/chat/instructions'; import { chatTools } from '@/services/chat/tools'; import { isOpenRouterModelId } from '@/lib/openrouter'; export const maxDuration = 300; export async function POST(request: Request) { try { const { messages, modelId } = await request.json(); if (modelId !== undefined && !isOpenRouterModelId(modelId)) { return Response.json({ error: 'Select a supported model' }, { status: 400 }); } if (!process.env.OPENROUTER_API_KEY) { return Response.json( { error: 'OPENROUTER_API_KEY is not configured' }, { status: 503 } ); } const model = getChatModel(modelId); const webSearchEnabled = Boolean(process.env.FIRECRAWL_API_KEY); const instructions = getChatInstructions(webSearchEnabled); const convertedMessages = await convertToModelMessages(messages); const prunedMessages = pruneMessages({ messages: convertedMessages, reasoning: 'all', toolCalls: 'none', }); const result = streamText({ model, instructions, messages: prunedMessages, abortSignal: request.signal, tools: webSearchEnabled ? chatTools : {}, stopWhen: stepCountIs(20), onError: ({ error }) => { if (!request.signal.aborted) console.error('Chat stream error:', error); }, providerOptions: { openrouter: { reasoning: { enabled: true, effort: 'medium', }, }, }, }); return result.toUIMessageStreamResponse({ sendReasoning: true, headers: { 'Cache-Control': 'no-cache, no-transform' }, onError: () => 'The response was interrupted. Please try again.', }); } catch (error) { console.error('Chat API error:', error); return new Response( JSON.stringify({ error: 'Failed to process chat request' }), { status: 500, headers: { 'Content-Type': 'application/json' }, } ); } }