fuentes automaticas: el CODIGO agrega la fuente, no el LLM (ronda 13-14)
- 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)
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
// 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}`
|
||||
}
|
||||
Reference in New Issue
Block a user