mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 23:53:05 +00:00
Modo "Por tema" no acervo de falas: expansão determinística do tema via DeepSeek (generateObject, timeout, fallback null) alimenta o motor lexical existente (searchText/keywords OR), mantendo facetas AND, paginação por fala, o OR de cortes do C174 e o access control intacto. Resultado de tema mostra selo e "Por que apareceu" com trecho real (frase contínua), sem score; degradação honesta para termo exato com aviso e vazio honesto com saídas. - URL: mode=tema (default omitido; canonicaliza sem q), seletor otimista - ai/expandSpeechSearchTheme + lib/speechThemeTerms (sanitiza saída do modelo) - view model com themeMatchTerm + espelho speechMatchesSearchTerm - UI do card de tema (selos, proveniência, ações) e aviso degradado - sem migration/Consent/exposição pública; gate de leitura inalterado - testes unit/int/e2e; design hi-fi estendido e certificado pelo designer
30 lines
1.1 KiB
TypeScript
30 lines
1.1 KiB
TypeScript
/**
|
|
* C192 — sanitization of the LLM-expanded theme terms before they reach the
|
|
* Payload `where`. The expansion is untrusted model output: `%`/`_`/`\` would
|
|
* widen the `LIKE`/`ILIKE` patterns, very short tokens produce noise, and the
|
|
* list has to stay bounded so the textual OR does not explode. Pure and
|
|
* client-safe.
|
|
*/
|
|
import { normalizeForSearch, uniqueByNormalizedForm } from '@/lib/speechSearch'
|
|
|
|
export const MAX_SPEECH_THEME_TERMS = 8
|
|
export const MIN_SPEECH_THEME_TERMS = 3
|
|
export const MAX_SPEECH_THEME_TERM_LENGTH = 60
|
|
const MIN_THEME_TERM_LENGTH = 3
|
|
const LIKE_WILDCARDS = /[%_\\]/g
|
|
|
|
const sanitizeSpeechThemeTerm = (value: string): string =>
|
|
value
|
|
.replace(LIKE_WILDCARDS, ' ')
|
|
.replace(/\s+/g, ' ')
|
|
.trim()
|
|
.slice(0, MAX_SPEECH_THEME_TERM_LENGTH)
|
|
.trim()
|
|
|
|
/** Removes wildcards, drops too-short/empty terms, dedupes and caps the list. */
|
|
export const normalizeSpeechThemeTerms = (values: readonly string[]): string[] =>
|
|
uniqueByNormalizedForm(
|
|
values
|
|
.map(sanitizeSpeechThemeTerm)
|
|
.filter((term) => normalizeForSearch(term).length >= MIN_THEME_TERM_LENGTH),
|
|
).slice(0, MAX_SPEECH_THEME_TERMS)
|