diff --git a/src/app/(campaign)/campanha/api/ai-chat/route.ts b/src/app/(campaign)/campanha/api/ai-chat/route.ts index 75fe9aad..9f83e4bf 100644 --- a/src/app/(campaign)/campanha/api/ai-chat/route.ts +++ b/src/app/(campaign)/campanha/api/ai-chat/route.ts @@ -9,11 +9,11 @@ import { } from 'ai' import { getPayload } from 'payload' +import { splitSollinhaFollowUpBlock } from '@/lib/sollinhaFollowUpSuggestions' 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' -import { splitSollinhaFollowUpBlock } from '@/lib/sollinhaFollowUpSuggestions' export const maxDuration = 60 diff --git a/src/components/campaign/shell/ai/CampaignAIChat.tsx b/src/components/campaign/shell/ai/CampaignAIChat.tsx index 12981d76..51e33380 100644 --- a/src/components/campaign/shell/ai/CampaignAIChat.tsx +++ b/src/components/campaign/shell/ai/CampaignAIChat.tsx @@ -18,8 +18,8 @@ import { cn } from '@/lib/utils' import { useAISidebar } from '@/components/campaign/shell/ai/CampaignAISidebarContext' import { ChatChipGroup } from '@/components/campaign/shell/ai/ChatChipGroup' import { useMicTranscript } from '@/components/campaign/shell/ai/useMicTranscript' -import { getSollinhaOpeningQuestions } from '@/lib/sollinhaOpeningQuestions' import { splitSollinhaFollowUpBlock } from '@/lib/sollinhaFollowUpSuggestions' +import { getSollinhaOpeningQuestions } from '@/lib/sollinhaOpeningQuestions' /** * B187+B188 — links in the assistant's markdown must look like links: brand @@ -221,9 +221,7 @@ export const CampaignAIChat = ({ className }: { className?: string }) => { {(showOpeningChips || showFollowUpChips) && ( ({ text })) + showOpeningChips ? openingQuestions : followUpSuggestions.map((text) => ({ text })) } onPick={({ text }) => { if (busy) return diff --git a/tests/e2e/campaignAiChatFollowUps.e2e.spec.ts b/tests/e2e/campaignAiChatFollowUps.e2e.spec.ts index 4b70d1d4..eff610f1 100644 --- a/tests/e2e/campaignAiChatFollowUps.e2e.spec.ts +++ b/tests/e2e/campaignAiChatFollowUps.e2e.spec.ts @@ -27,8 +27,11 @@ const THIRD_RESPONSE = `Os dados de 2026 ainda não existem.` const lastUserText = (body: unknown): string | null => { const messages = - (body as { messages?: Array<{ role?: string; parts?: Array<{ type?: string; text?: string }> }> }) - ?.messages ?? [] + ( + body as { + messages?: Array<{ role?: string; parts?: Array<{ type?: string; text?: string }> }> + } + )?.messages ?? [] for (const message of [...messages].reverse()) { if (message.role !== 'user') continue for (const part of message.parts ?? []) { @@ -42,10 +45,11 @@ const mockAiChat = (page: Page) => page.route('**/campanha/api/ai-chat', async (route) => { const raw = route.request().postData() ?? '' const userText = lastUserText(JSON.parse(raw || '{}')) - const response = - userText?.includes('Ilhéus') ? FIRST_RESPONSE - : userText?.includes('Salvador em 2022?') ? SECOND_RESPONSE - : THIRD_RESPONSE + const response = userText?.includes('Ilhéus') + ? FIRST_RESPONSE + : userText?.includes('Salvador em 2022?') + ? SECOND_RESPONSE + : THIRD_RESPONSE await route.fulfill({ status: 200, headers: { 'Content-Type': 'text/event-stream' }, @@ -157,16 +161,18 @@ test.describe('B192 — follow-ups sugeridos após cada resposta do Sollinha', ( await page.getByRole('button', { name: 'Sollinha — Assistente virtual' }).click() await expect(drawer).toBeVisible({ timeout: 20_000 }) - await askViaInput(page, 'Quantos votos tivemos em Ilhéus em 2022?', 'Em 2022, o Solla teve 12.345 votos em Ilhéus.') + await askViaInput( + page, + 'Quantos votos tivemos em Ilhéus em 2022?', + 'Em 2022, o Solla teve 12.345 votos em Ilhéus.', + ) await expect(page.getByText('Em 2022, o Solla teve 12.345 votos em Ilhéus.')).toBeVisible({ timeout: 20_000, }) await expect(chipButton(page, 'Quantos votos tivemos em Salvador em 2022?')).toBeVisible() await expect(chipButton(page, 'Quem foi o deputado mais votado em Barreiras?')).toBeVisible() - await expect( - chipButton(page, 'Como está o município de Vitória da Conquista?'), - ).toHaveCount(0) + await expect(chipButton(page, 'Como está o município de Vitória da Conquista?')).toHaveCount(0) await expect(page.getByText('Sugestões de continuação')).toHaveCount(0) }) diff --git a/tests/unit/campaignSollinhaFollowUpSuggestions.unit.spec.ts b/tests/unit/campaignSollinhaFollowUpSuggestions.unit.spec.ts index 3028c809..7811d4c9 100644 --- a/tests/unit/campaignSollinhaFollowUpSuggestions.unit.spec.ts +++ b/tests/unit/campaignSollinhaFollowUpSuggestions.unit.spec.ts @@ -74,10 +74,7 @@ describe('splitSollinhaFollowUpBlock', () => { it('deduplicates repeated items', () => { const text = `Resposta.\n\n${marker}\n- Mesma pergunta?\n- Mesma pergunta?\n- Outra?` - expect(splitSollinhaFollowUpBlock(text).suggestions).toEqual([ - 'Mesma pergunta?', - 'Outra?', - ]) + expect(splitSollinhaFollowUpBlock(text).suggestions).toEqual(['Mesma pergunta?', 'Outra?']) }) it('handles CRLF line endings', () => { @@ -93,7 +90,9 @@ describe('splitSollinhaFollowUpBlock', () => { it('uses the LAST occurrence of the marker when the model repeats it', () => { const text = `Resposta.\n\n${marker}\n- Antigas?\n- Também antigas?\n\nMais corpo.\n\n${marker}\n- Novas?\n- Atuais?` const { body, suggestions } = splitSollinhaFollowUpBlock(text) - expect(body).toBe('Resposta.\n\n**Sugestões de continuação:**\n- Antigas?\n- Também antigas?\n\nMais corpo.') + expect(body).toBe( + 'Resposta.\n\n**Sugestões de continuação:**\n- Antigas?\n- Também antigas?\n\nMais corpo.', + ) expect(suggestions).toEqual(['Novas?', 'Atuais?']) })