Files
SKEEN-Proyecto/wacrm/src/app/api/ai/knowledge/reindex/route.ts
Consultoría Alcaraz Salazar a718592291 Initial commit: SKEEN Derma Experts - Sistema Integral de Gestión Clínica
- Frontend React (SKEEN Brand) con Vite, TypeScript, Tailwind
- Frontend Homenest (versión alternativa)
- Módulos Odoo 17 custom (citas, pacientes, monedero, pagos, ventas, inventario, whatsapp)
- WACRM fork (Next.js 16 + Supabase)
- Hermes + Bridge + Skills (Qwen3.6 via Nan Builders)
- Scripts de migración y operación
- Documentación extensiva en docs/
2026-07-20 07:44:23 +00:00

80 lines
2.7 KiB
TypeScript

import { NextResponse } from 'next/server'
import { requireRole, toErrorResponse } from '@/lib/auth/account'
import { checkRateLimit, rateLimitResponse, RATE_LIMITS } from '@/lib/rate-limit'
import { loadEmbeddingsKey } from '@/lib/ai/config'
import { ingestDocument } from '@/lib/ai/knowledge'
import { AiError } from '@/lib/ai/types'
/**
* POST /api/ai/knowledge/reindex (admin+)
*
* Re-chunk and re-embed every document in the account. The main use is
* after adding an embeddings key: existing documents were stored
* lexical-only, and this backfills their vectors so semantic search
* turns on. Also recovers documents whose indexing failed earlier.
*/
export async function POST() {
try {
const { supabase, accountId, userId } = await requireRole('admin')
const limit = checkRateLimit(`ai-kb-reindex:${userId}`, RATE_LIMITS.adminAction)
if (!limit.success) return rateLimitResponse(limit)
const { data: docs, error } = await supabase
.from('ai_knowledge_documents')
.select('id, content')
.eq('account_id', accountId)
if (error) {
console.error('[ai/knowledge/reindex] fetch error:', error)
return NextResponse.json(
{ error: 'Failed to load documents' },
{ status: 500 },
)
}
const { key: embeddingsApiKey, corrupt } = await loadEmbeddingsKey(
supabase,
accountId,
)
// The whole point of Reindex is usually to backfill embeddings — so
// if a key is configured but can't be decrypted, don't quietly do a
// lexical-only pass and report success. Stop and tell the admin.
if (corrupt) {
return NextResponse.json(
{
success: false,
reindexed: 0,
error:
'Your embeddings key could not be decrypted (check ENCRYPTION_KEY, then re-enter the key in Settings → AI Assistant). Nothing was reindexed.',
},
{ status: 200 },
)
}
let reindexed = 0
for (const doc of docs ?? []) {
try {
await ingestDocument(supabase, accountId, { embeddingsApiKey }, doc.id, doc.content)
reindexed += 1
} catch (err) {
// One bad document (e.g. a mid-run embeddings rate-limit) should
// not abort the whole batch.
const message = err instanceof AiError ? err.message : String(err)
console.error(`[ai/knowledge/reindex] doc ${doc.id} failed:`, message)
return NextResponse.json(
{
success: false,
reindexed,
total: (docs ?? []).length,
error: `Reindexed ${reindexed}, then hit an error: ${message}`,
},
{ status: 200 },
)
}
}
return NextResponse.json({ success: true, reindexed })
} catch (err) {
return toErrorResponse(err)
}
}