fix(rag): scan full NOMAD_DATA_PATH for PDFs, not only 10_EIGENE_PDFS_RAG

Made-with: Cursor
This commit is contained in:
Florian Neuhuber 2026-03-16 22:00:03 +01:00
parent 6db599ecc3
commit 0c3fc53f9b
2 changed files with 45 additions and 8 deletions

View File

@ -4,6 +4,7 @@ import { inject } from '@adonisjs/core'
import logger from '@adonisjs/core/services/logger'
import { TokenChunker } from '@chonkiejs/core'
import sharp from 'sharp'
import env from '@adonisjs/core/services/env'
import { deleteFileIfExists, determineFileType, getFile, getFileStatsIfExists, listDirectoryContentsRecursive, ZIM_STORAGE_PATH } from '../utils/fs.js'
import { PDFParse } from 'pdf-parse'
import { createWorker } from 'tesseract.js'
@ -988,17 +989,16 @@ export class RagService {
logger.info(`[RAG] Deleted all points for source: ${source}`)
/** Delete the physical file only if it lives inside the uploads directory.
* resolve() normalises path traversal sequences (e.g. "/../..") before the
* check to prevent path traversal vulns
* The trailing sep is to ensure a prefix like "kb_uploads_{something_incorrect}" can't slip through.
* For NOMAD_DATA files we only remove from the index; we do not delete the file on disk (user-owned data).
* resolve() normalises path traversal sequences (e.g. "/../..") before the check to prevent path traversal vulns.
*/
const uploadsAbsPath = join(process.cwd(), RagService.UPLOADS_STORAGE_PATH)
const resolvedSource = resolve(source)
if (resolvedSource.startsWith(uploadsAbsPath + sep)) {
if (resolvedSource.startsWith(resolve(uploadsAbsPath) + sep)) {
await deleteFileIfExists(resolvedSource)
logger.info(`[RAG] Deleted uploaded file from disk: ${resolvedSource}`)
} else {
logger.warn(`[RAG] File was removed from knowledge base but doesn't live in Nomad's uploads directory, so it can't be safely removed. Skipping deletion of physical file...`)
logger.debug(`[RAG] Removed from index only (file not in uploads dir): ${resolvedSource}`)
}
return { success: true, message: 'File removed from knowledge base.' }
@ -1125,6 +1125,29 @@ export class RagService {
}
}
// Scan NOMAD-DATA (ganzer Ordner: 01_MEDIZIN, 04_SURVIVAL, 10_EIGENE_PDFS_RAG, etc.) für RAG
const nomadDataPath = env.get('NOMAD_DATA_PATH')
if (nomadDataPath) {
const nomadDataAbs = nomadDataPath.startsWith(sep) ? resolve(nomadDataPath) : join(process.cwd(), nomadDataPath)
try {
const nomadContents = await listDirectoryContentsRecursive(nomadDataAbs)
let nomadCount = 0
nomadContents.forEach((entry) => {
if (entry.type === 'file' && determineFileType(entry.name) !== 'unknown') {
filesInStorage.push(entry.key)
nomadCount++
}
})
logger.debug(`[RAG] Found ${nomadCount} supported files in NOMAD-DATA`)
} catch (error) {
if (error.code === 'ENOENT') {
logger.debug(`[RAG] NOMAD-DATA path ${nomadDataAbs} does not exist, skipping`)
} else {
logger.warn(`[RAG] Error scanning NOMAD-DATA:`, error)
}
}
}
logger.info(`[RAG] Found ${filesInStorage.length} total files in storage directories`)
// Get all stored sources from Qdrant

View File

@ -2,6 +2,21 @@
Dieses Skript lädt alle in `nomad-data-pdf-urls.txt` eingetragenen PDFs und Ressourcen in eine NOMAD-DATA-Ordnerstruktur.
## Warum sehe ich die neuen ZIM-Kategorien / PDFs nicht?
- **ZIM-Kategorien (Deutsch & Österreich, Klexikon, Koch-Wiki, …):** Die App lädt die Collection-Dateien von einer URL. Standard ist der **main**-Branch von Crosstalk-Solutions dort sind die neuen Kategorien erst nach Merge des PRs. Damit du sie **sofort** siehst, muss die laufende NOMAD-Instanz die Umgebungsvariable **`NOMAD_COLLECTIONS_BASE_URL`** setzen (z.B. in der Docker-/Compose-Umgebung):
```bash
NOMAD_COLLECTIONS_BASE_URL=https://raw.githubusercontent.com/neuhubereco/project-nomad/refs/heads/feature/collections-german-austria-zim
```
Danach in der Oberfläche unter Einstellungen / Content Explorer die Collections neu laden („Collections aktualisieren“ o.ä.).
**Hinweis:** Die App-Version muss den Code für `NOMAD_COLLECTIONS_BASE_URL` enthalten (z.B. Build von deinem Fork oder nach Merge des PRs).
- **PDFs in der Knowledge Base:** Die App muss **`NOMAD_DATA_PATH`** auf den Ordner setzen, in dem deine NOMAD-DATA-Struktur liegt (z.B. wo die heruntergeladenen PDFs in 01_MEDIZIN, 04_SURVIVAL, … liegen). Dieser Pfad muss **im Container** erreichbar sein (z.B. Volume-Mount):
```bash
NOMAD_DATA_PATH=/storage/nomad_data
```
Wenn die PDFs auf dem Host unter `/home/nomad/nomad_data` liegen, muss dieses Verzeichnis z.B. als `/storage/nomad_data` in den Admin-Container gemountet sein. Anschließend in der App: **Knowledge Base → Scan and Sync** ausführen.
## 1. Live: Neue ZIM-Kategorien
Die neuen ZIM-Kategorien (Deutsch & Österreich, Militär & Taktik, Kommunikation, Energie & Off-Grid) sind in `collections/kiwix-categories.json` eingetragen.
@ -43,12 +58,11 @@ Es werden u.a. geladen:
## 3. RAG (Knowledge Base)
Die App indexiert nur Inhalte unter **`NOMAD_DATA_PATH/10_EIGENE_PDFS_RAG`**.
Die App indexiert alle unterstützten Dateien (PDF, Text, Bilder) **im gesamten Ordner** `NOMAD_DATA_PATH` (also auch 01_MEDIZIN, 04_SURVIVAL, 07_FUNK, 08_VORRAT, 10_EIGENE_PDFS_RAG usw.).
Wenn du die heruntergeladenen PDFs auch in der Knowledge Base nutzen willst:
- Entweder **Kopien/Symlinks** der gewünschten PDFs nach `NOMAD-DATA/10_EIGENE_PDFS_RAG/` legen (z.B. Unterordner `Survival`, `Funk`, `Medizin`),
- oder `NOMAD_DATA_PATH` auf deinen NOMAD-DATA-Root setzen und in der App nur diesen einen Baum nutzen; dann müsste die RAG-Logik um weitere Ordner (z.B. 04_SURVIVAL, 07_FUNK) erweitert werden.
- `NOMAD_DATA_PATH` auf deinen NOMAD-DATA-Root setzen (z.B. `/opt/project-nomad/storage/nomad_data` oder `/home/nomad/nomad_data`). Die RAG-Sync durchsucht dann **alle** Unterordner (01_MEDIZIN, 04_SURVIVAL, 07_FUNK, …).
Anschließend in der App: **Knowledge Base → Scan and Sync**.