teqo-backup/src/lib/acervoListSort.ts
Francisco Solla 3e8429daae feat(C229): busca por sentido no acervo de falas
Troca o motor do modo tema (Câmara + internet) de expansão lexical para
embeddings (Deep Infra, BAAI/bge-m3), com índice próprio (speechEmbedding),
ranking por proximidade em memória, evidência real do trecho mais próximo,
vazio honesto por limiar e degradação para a busca literal. Inclui a
collection oculta + migration, o CLI pnpm acervo:index (--dry-run/--limit/
--source/--force/--probe), o port do design aprovado e a suíte de testes.

Pós-rebase: o snapshot da migration seguinte (20260926_062039_add_archive_photo,
do C231 já na main) foi regenerado para incluir speech_embedding — sem ele o
próximo migrate:create recriaria a tabela.

Closes #1360
2026-09-26 11:59:21 -03:00

63 lines
2.6 KiB
TypeScript

/**
* C216 — the ONE ordering vocabulary of the acervo list: the recordings source
* (C219) and the web speeches source (C216) share the options, the labels, the
* `recentes` default (never serialized) and the duration gate. Each domain owns
* the mapping to its Payload order (recordings default `-createdAt`, web
* `-speechAt`); a duration order only lists rows with a measured duration,
* because Postgres sorts NULLS FIRST on DESC.
*/
export const ACERVO_SORT_OPTIONS = [
{ value: 'recentes', label: 'Mais recentes' },
{ value: 'duracao_maior', label: 'Duração (maior)' },
{ value: 'duracao_menor', label: 'Duração (menor)' },
] as const
export type AcervoSortKey = (typeof ACERVO_SORT_OPTIONS)[number]['value']
const ACERVO_SORT_SET = new Set<string>(ACERVO_SORT_OPTIONS.map(({ value }) => value))
const firstParam = (value: string | string[] | undefined): string | undefined =>
Array.isArray(value) ? value[0] : value
/**
* Only the non-default orders are meaningful; anything else (and `recentes`) is the default.
*/
export const parseAcervoSort = (raw: string | string[] | undefined): AcervoSortKey | undefined => {
const value = firstParam(raw)
return value && value !== 'recentes' && ACERVO_SORT_SET.has(value)
? (value as AcervoSortKey)
: undefined
}
/**
* C229 — the ordering vocabulary of the theme mode of the speech acervo:
* relevance is the default (never serialized) and the explicit date/duration
* order must stay askable. Recordings and the public Central keep
* `ACERVO_SORT_OPTIONS` — their theme mode is not the semantic engine.
*/
export const ACERVO_THEME_SORT_OPTIONS = [
{ value: 'relevancia', label: 'Mais relevantes' },
...ACERVO_SORT_OPTIONS,
] as const
export type AcervoThemeSortKey = (typeof ACERVO_THEME_SORT_OPTIONS)[number]['value']
const ACERVO_THEME_SORT_SET = new Set<string>(ACERVO_THEME_SORT_OPTIONS.map(({ value }) => value))
/**
* Theme-aware parse: `relevancia` (and anything unknown) parses away as the
* default, while `recentes` is a REAL state here — the exact-mode default has
* to stay explicitly askable after relevance became the theme default.
*/
export const parseAcervoThemeSort = (
raw: string | string[] | undefined,
): AcervoSortKey | undefined => {
const value = firstParam(raw)
if (!value || value === 'relevancia' || !ACERVO_THEME_SORT_SET.has(value)) return undefined
return value as AcervoSortKey
}
/** Duration orders only make sense over rows with a measured duration. */
export const acervoSortIsDuration = (sort: AcervoSortKey | undefined): boolean =>
sort === 'duracao_maior' || sort === 'duracao_menor'