- sources.mjs (nuevo): selectSource() + attribute() — seleccion de material
≤72h con score≥6, atribucion en formato fijo.
- core.mjs: las 4 piezas (digest/opinion/reflection/events) generan sobre
la fuente seleccionada y el codigo agrega la atribucion DESPUES del LLM.
- guard.mjs:
* R1/R2/R4/R5/R8 evaluan el TEXTO SIN la linea de atribucion
* R3/R7 ven el texto completo (con atribucion) — la fuente garantiza
que pasen
* R9 (nueva): la pieza debe compartir terminos del dominito con su fuente
* R8: superlativos estrechos (sin never/only que dejaban pasar aforismos)
* deletion anadido a METADATA_TYPES (no cuenta hacia el tope diario)
- guard.test.mjs: 24/24 verdes (incluye tests R14a/c/d)
46 lines
2.2 KiB
JavaScript
46 lines
2.2 KiB
JavaScript
// sources.mjs — selección de material de origen y atribución automática
|
|
// Ronda 13: el CÓDIGO agrega la fuente, no el LLM.
|
|
import { readFileSync } from 'node:fs'
|
|
import { createRequire } from 'node:module'
|
|
const require_ = createRequire(import.meta.url)
|
|
|
|
let _pool = null
|
|
function pool() {
|
|
if (!_pool) {
|
|
const pg = require_('/srv/cayce-core/node_modules/pg')
|
|
const env = Object.fromEntries(
|
|
readFileSync('/srv/hardcap-news/backend/.env', 'utf8').split('\n').filter(l => l.includes('=')).map(l => [l.slice(0, l.indexOf('=')), l.slice(l.indexOf('=') + 1)])
|
|
)
|
|
_pool = new pg.Pool({ connectionString: env.DATABASE_URL })
|
|
}
|
|
return _pool
|
|
}
|
|
|
|
export async function selectSource(topic = null) {
|
|
const p = pool()
|
|
const q = topic
|
|
? `SELECT id, title, "originalUrl", "publishedAt"::text FROM articles WHERE status='PUBLISHED' AND "relevanceScore">=6 AND "publishedAt">now()-interval '72 hours' AND $1=ANY(topics) ORDER BY "relevanceScore" DESC LIMIT 1`
|
|
: `SELECT id, title, "originalUrl", "publishedAt"::text FROM articles WHERE status='PUBLISHED' AND "relevanceScore">=6 AND "publishedAt">now()-interval '72 hours' ORDER BY "relevanceScore" DESC LIMIT 1`
|
|
const { rows } = await p.query(q, topic ? [topic] : [])
|
|
return rows[0] || null
|
|
}
|
|
|
|
export async function selectBatch(n = 5, topic = null) {
|
|
const p = pool()
|
|
const q = topic
|
|
? `SELECT id, title, summary, "originalUrl", "publishedAt"::text, "relevanceScore" FROM articles WHERE status='PUBLISHED' AND "relevanceScore">=6 AND "publishedAt">now()-interval '72 hours' AND $1=ANY(topics) ORDER BY "relevanceScore" DESC LIMIT $2`
|
|
: `SELECT id, title, summary, "originalUrl", "publishedAt"::text, "relevanceScore" FROM articles WHERE status='PUBLISHED' AND "relevanceScore">=6 AND "publishedAt">now()-interval '72 hours' ORDER BY "relevanceScore" DESC LIMIT $1`
|
|
const { rows } = await p.query(q, topic ? [topic, n] : [n])
|
|
return rows
|
|
}
|
|
|
|
export function attribute(source) {
|
|
if (!source) return ''
|
|
const date = (source.publishedAt || '').slice(0, 10) || new Date().toISOString().slice(0, 10)
|
|
return `\n\n— fuente: ${source.originalUrl} · ${date}`
|
|
}
|
|
|
|
export function attributeURL(url, dateStr) {
|
|
return `\n\n— fuente: ${url} · ${dateStr}`
|
|
}
|