2_000_000) { dbnToolsError('content exceeds 2 MB limit.', 400, 'too_large'); } // Load CaveauAI platform (getDb, ClientRagPipeline, etc.) dbnToolsBootCaveau(); try { $db = getDb(); } catch (Throwable $e) { dbnToolsError('CaveauAI database unavailable: ' . $e->getMessage(), 503, 'db_unavailable'); } // Resolve default corpus for this client $stmt = $db->prepare('SELECT id FROM client_corpora WHERE client_id = ? AND is_default = 1 LIMIT 1'); $stmt->execute([$clientId]); $corpusId = (int)($stmt->fetchColumn() ?: 0); if ($corpusId === 0) { dbnToolsError( 'No default corpus found for your account. Set one up in the CaveauAI portal.', 409, 'no_corpus' ); } $wordCount = str_word_count($content); $ins = $db->prepare(" INSERT INTO client_documents (client_id, corpus_id, title, source_type, content, category, tags, import_method, source_tool, word_count, status) VALUES (?, ?, ?, 'text', ?, 'tool-output', ?, 'tool_output', ?, ?, 'pending') "); $ins->execute([$clientId, $corpusId, $title, $content, $tags, $sourceTool, $wordCount]); $docId = (int)$db->lastInsertId(); try { $rag = new ClientRagPipeline($clientId); $chunks = $rag->ingestDocument($docId); } catch (Throwable $e) { // Document is saved but not indexed — mark error and return partial success $db->prepare("UPDATE client_documents SET status='error', error_message=? WHERE id=?") ->execute([$e->getMessage(), $docId]); dbnToolsError( 'Saved to corpus but indexing failed: ' . $e->getMessage(), 500, 'index_failed', ['document_id' => $docId] ); } dbnToolsRespond(['ok' => true, 'document_id' => $docId, 'chunks' => $chunks], 201);