- Streaming AI endpoint at /campanha/api/ai-chat with DeepSeek V4 Flash - 8 tools: calculate, getMunicipalityVotes, getTopDeputies, getDobradinhas, getMunicipalityOverview, getLeaderships, getOrganizations, searchEntities - In-memory rate limiter (50 msgs / 15 min per user) - Desktop: resizable right sidebar via react-resizable-panels - Mobile: full-screen Drawer opened by FAB (2/3 size of quick-actions FAB) - Markdown rendering with react-markdown + remark-gfm (tables, bold, lists) - Session-bound context (clean slate each open) - Auth via existing campaign-token cookie, tools respect user RBAC - Architecture doc: docs/plans/ai-chat-sollinha.md - E2E tests: campaignAiChat.e2e.spec.ts (mocked endpoint)
51 lines
1.4 KiB
TypeScript
51 lines
1.4 KiB
TypeScript
import { deepSeek } from '@ai-sdk/deepseek'
|
|
import configPromise from '@payload-config'
|
|
import {
|
|
convertToModelMessages,
|
|
createUIMessageStreamResponse,
|
|
stepCountIs,
|
|
streamText,
|
|
toUIMessageStream,
|
|
} from 'ai'
|
|
import { getPayload } from 'payload'
|
|
|
|
import { checkRateLimit } from '@/utilities/ai/rateLimit'
|
|
import { AI_SYSTEM_PROMPT } from '@/utilities/ai/systemPrompt'
|
|
import { buildAITools } from '@/utilities/ai/tools'
|
|
import { getCampaignUserRaw } from '@/utilities/campaignAuth'
|
|
|
|
export const maxDuration = 60
|
|
|
|
export async function POST(req: Request) {
|
|
// Authenticate
|
|
const user = await getCampaignUserRaw()
|
|
if (!user) {
|
|
return Response.json({ error: 'Não autenticado.' }, { status: 401 })
|
|
}
|
|
|
|
// Rate limit
|
|
if (!checkRateLimit(user.id)) {
|
|
return Response.json(
|
|
{
|
|
error: 'Você atingiu o limite de mensagens. Aguarde alguns minutos e tente novamente.',
|
|
},
|
|
{ status: 429 },
|
|
)
|
|
}
|
|
|
|
const payload = await getPayload({ config: configPromise })
|
|
|
|
const { messages } = await req.json()
|
|
|
|
const result = streamText({
|
|
model: deepSeek('deepseek-v4-flash'),
|
|
system: AI_SYSTEM_PROMPT,
|
|
messages: await convertToModelMessages(messages),
|
|
tools: buildAITools({ user, payload }),
|
|
stopWhen: stepCountIs(10),
|
|
})
|
|
|
|
return createUIMessageStreamResponse({
|
|
stream: toUIMessageStream({ stream: result.stream }),
|
|
})
|
|
}
|