mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
129 lines
4 KiB
TypeScript
129 lines
4 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
const { generateObjectMock, deepSeekMock } = vi.hoisted(() => ({
|
|
generateObjectMock: vi.fn(),
|
|
deepSeekMock: vi.fn(() => 'mock-model'),
|
|
}))
|
|
|
|
vi.mock('ai', () => ({ generateObject: generateObjectMock }))
|
|
vi.mock('@ai-sdk/deepseek', () => ({ deepSeek: deepSeekMock }))
|
|
|
|
import {
|
|
rerankSpeechExcerpts,
|
|
type SpeechExcerptRerankCandidate,
|
|
} from '@/utilities/ai/rerankSpeechExcerpts'
|
|
|
|
const candidate = (index: number): SpeechExcerptRerankCandidate => ({
|
|
index,
|
|
dateLabel: '07/02/2023 · 17:28',
|
|
durationSeconds: 20,
|
|
citation: 'A saúde pública baiana',
|
|
})
|
|
|
|
const candidates = [candidate(0), candidate(1), candidate(2)]
|
|
|
|
const rerank = (overrides: Partial<Parameters<typeof rerankSpeechExcerpts>[0]> = {}) =>
|
|
rerankSpeechExcerpts({
|
|
intencao: 'reels curto sobre o hospital do subúrbio',
|
|
tema: 'hospital do subúrbio',
|
|
candidates,
|
|
limit: 3,
|
|
...overrides,
|
|
})
|
|
|
|
beforeEach(() => {
|
|
process.env.DEEPSEEK_API_KEY = 'test-deepseek-key'
|
|
generateObjectMock.mockReset()
|
|
deepSeekMock.mockClear()
|
|
})
|
|
|
|
afterEach(() => {
|
|
delete process.env.DEEPSEEK_API_KEY
|
|
})
|
|
|
|
describe('rerankSpeechExcerpts (C158)', () => {
|
|
it('filtra índices fora do pool/duplicados e normaliza o motivo', async () => {
|
|
generateObjectMock.mockResolvedValue({
|
|
object: {
|
|
choices: [
|
|
{ index: 0, reason: ' Cita o hospital e fecha a ideia ' },
|
|
{ index: 9, reason: 'fora do pool' },
|
|
{ index: 0, reason: 'duplicado' },
|
|
{ index: 2, reason: 'ok' },
|
|
],
|
|
},
|
|
})
|
|
|
|
await expect(rerank()).resolves.toEqual({
|
|
choices: [
|
|
{ index: 0, reason: 'Cita o hospital e fecha a ideia' },
|
|
{ index: 2, reason: 'ok' },
|
|
],
|
|
})
|
|
})
|
|
|
|
it('respeita o limite de escolhas', async () => {
|
|
generateObjectMock.mockResolvedValue({
|
|
object: {
|
|
choices: [
|
|
{ index: 0, reason: 'a' },
|
|
{ index: 1, reason: 'b' },
|
|
{ index: 2, reason: 'c' },
|
|
],
|
|
},
|
|
})
|
|
|
|
await expect(rerank({ limit: 1 })).resolves.toEqual({
|
|
choices: [{ index: 0, reason: 'a' }],
|
|
})
|
|
})
|
|
|
|
it('honra a lista vazia como decisão ("nenhum serve")', async () => {
|
|
generateObjectMock.mockResolvedValue({ object: { choices: [] } })
|
|
|
|
await expect(rerank()).resolves.toEqual({ choices: [] })
|
|
})
|
|
|
|
it('devolve null quando todos os motivos vêm vazios ou os índices são inválidos', async () => {
|
|
generateObjectMock.mockResolvedValue({
|
|
object: { choices: [{ index: 0, reason: ' ' }] },
|
|
})
|
|
|
|
await expect(rerank()).resolves.toBeNull()
|
|
})
|
|
|
|
it('corta o motivo em 140 caracteres', async () => {
|
|
generateObjectMock.mockResolvedValue({
|
|
object: { choices: [{ index: 1, reason: 'x'.repeat(500) }] },
|
|
})
|
|
|
|
const result = await rerank()
|
|
expect(result?.choices[0]!.reason).toHaveLength(140)
|
|
})
|
|
|
|
it('curto-circuita sem chave, sem pool ou em erro do provedor — nunca lança', async () => {
|
|
await expect(rerank({ candidates: [] })).resolves.toBeNull()
|
|
expect(generateObjectMock).not.toHaveBeenCalled()
|
|
|
|
delete process.env.DEEPSEEK_API_KEY
|
|
await expect(rerank()).resolves.toBeNull()
|
|
expect(generateObjectMock).not.toHaveBeenCalled()
|
|
|
|
process.env.DEEPSEEK_API_KEY = 'test-deepseek-key'
|
|
generateObjectMock.mockRejectedValue(new Error('provider down'))
|
|
await expect(rerank()).resolves.toBeNull()
|
|
})
|
|
|
|
it('envia intenção, tema e citações truncadas ao modelo', async () => {
|
|
generateObjectMock.mockResolvedValue({ object: { choices: [{ index: 0, reason: 'ok' }] } })
|
|
|
|
await rerank({ candidates: [candidate(0), { ...candidate(1), citation: 'y'.repeat(900) }] })
|
|
|
|
const call = generateObjectMock.mock.calls[0]![0] as { prompt: string; abortSignal: unknown }
|
|
expect(call.prompt).toContain('reels curto sobre o hospital do subúrbio')
|
|
expect(call.prompt).toContain('Tema: hospital do subúrbio')
|
|
expect(call.prompt).toContain('[0]')
|
|
expect(call.prompt).not.toContain('y'.repeat(701))
|
|
expect(call.abortSignal).toBeInstanceOf(AbortSignal)
|
|
})
|
|
})
|