You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Suite à #251, le StoreKind.NEO4J est exposé côté domaine + service + UI : on peut créer / éditer / supprimer un Store de kind Neo4j, et la config (index_name, database) est validée et persistée. Mais aucun adaptateur d'ingestion n'est branché : seul OpenSearchStore (document-parser/infra/opensearch_store.py) implémente le port VectorStore. Conséquence : un Store Neo4j créé via l'UI est inerte — le pipeline d'ingestion ne sait pas pousser dedans.
L'infrastructure Neo4j existe déjà partiellement : infra/neo4j.py gère le driver async (auth via NEO4J_URI/NEO4J_USER/NEO4J_PASSWORD) et bootstrap_schema(), utilisé aujourd'hui pour le graphe-natif de structure documentaire (#202+). Cette issue ajoute un second usage du driver : un adaptateur de chunks-vector-store.
Scope
In scope
infra/neo4j_store.py — implémente le port VectorStore (méthodes index_chunks, search, delete_for_doc, health) en utilisant le driver Neo4j 5.x et son support natif des vector indexes.
Création idempotente du vector index au premier push (CREATE VECTOR INDEX <name> IF NOT EXISTS FOR (c:Chunk) ON c.embedding OPTIONS {…}), avec dimension lue depuis EMBEDDING_DIMENSION.
Schéma minimal : nœuds (:Chunk {chunk_id, doc_id, text, page, headings, embedding}) rattachés à un nœud (:Document {doc_id}) via [:HAS_CHUNK]. La database du Store est routée à la session (session(database=…)).
Sélection de l'adaptateur côté IngestionService selon Store.kind au lieu d'une instance globale OpenSearchStore.
Tests : unitaires (mocked driver) + un test d'intégration optionnel gardé par NEO4J_URI env (skip si absent).
Out of scope
Page de query (/index/:slug/query) — toujours hors périmètre.
L'ingestion (POST /api/ingestion/{analysis_id}) ne consulte pas Store.kind ; elle pousse uniquement vers l'instance globale OpenSearchStore configurée par OPENSEARCH_URL. Un Store Neo4j est donc visible côté UI mais inerte.
Expected behavior
IngestionService lit Store.kind (et Store.config) pour choisir l'adaptateur :
opensearch → OpenSearchStore (existant)
neo4j → Neo4jStore (à créer)
Push d'un document vers un Store Neo4j → chunks indexés dans le vector index Neo4j configuré, recherche par similarité fonctionnelle (cosine/euclidean selon OPTIONS).
health() retourne True si le driver répond, False + errorMessage sinon → propagé jusqu'à StoreInfoResponse.connected côté UI.
Tous les tests passent (pytest), zéro violation lint.
Impact / Priority rationale
Sans cet adaptateur, l'option « Neo4j » dans le form est trompeuse : elle laisse créer un Store qui n'ingère rien. Soit on livre l'adaptateur, soit on retire l'option du dropdown jusqu'à ce qu'elle soit fonctionnelle. À programmer pour 0.7.0 (pas urgent — pas de régression sur l'existant).
Context
Suite à #251, le
StoreKind.NEO4Jest exposé côté domaine + service + UI : on peut créer / éditer / supprimer un Store de kind Neo4j, et la config (index_name,database) est validée et persistée. Mais aucun adaptateur d'ingestion n'est branché : seulOpenSearchStore(document-parser/infra/opensearch_store.py) implémente le portVectorStore. Conséquence : un Store Neo4j créé via l'UI est inerte — le pipeline d'ingestion ne sait pas pousser dedans.L'infrastructure Neo4j existe déjà partiellement :
infra/neo4j.pygère le driver async (auth viaNEO4J_URI/NEO4J_USER/NEO4J_PASSWORD) etbootstrap_schema(), utilisé aujourd'hui pour le graphe-natif de structure documentaire (#202+). Cette issue ajoute un second usage du driver : un adaptateur de chunks-vector-store.Scope
In scope
infra/neo4j_store.py— implémente le portVectorStore(méthodesindex_chunks,search,delete_for_doc,health) en utilisant le driver Neo4j 5.x et son support natif des vector indexes.CREATE VECTOR INDEX <name> IF NOT EXISTS FOR (c:Chunk) ON c.embedding OPTIONS {…}), avec dimension lue depuisEMBEDDING_DIMENSION.(:Chunk {chunk_id, doc_id, text, page, headings, embedding})rattachés à un nœud(:Document {doc_id})via[:HAS_CHUNK]. Ladatabasedu Store est routée à la session (session(database=…)).IngestionServiceselonStore.kindau lieu d'une instance globaleOpenSearchStore.NEO4J_URIenv (skip si absent).Out of scope
/index/:slug/query) — toujours hors périmètre.Store.config) — pour l'instant l'auth reste globale via env.StoreNeo4j existants (l'enum a été ajouté dans [ENHANCEMENT] CRUD complet sur Stores (fix 404 + management des configs) #251 ; pas de stores en prod).Current behavior
POST /api/ingestion/{analysis_id}) ne consulte pasStore.kind; elle pousse uniquement vers l'instance globaleOpenSearchStoreconfigurée parOPENSEARCH_URL. Un Store Neo4j est donc visible côté UI mais inerte.Expected behavior
IngestionServicelitStore.kind(etStore.config) pour choisir l'adaptateur :opensearch→OpenSearchStore(existant)neo4j→Neo4jStore(à créer)OPTIONS).health()retourneTruesi le driver répond,False+errorMessagesinon → propagé jusqu'àStoreInfoResponse.connectedcôté UI.Steps to reproduce / Acceptance criteria
infra/neo4j_store.pyimplémenteVectorStore(port domain).IngestionServiceroute vers l'adaptateur selonStore.kind.NEO4J_URIabsent) — push doc, search, delete, vérifier comptage.StoreInfoResponse.connectedreflète l'état réel du driver (UI montre « Connecté/Déconnecté »).pytest), zéro violation lint.Impact / Priority rationale
Sans cet adaptateur, l'option « Neo4j » dans le form est trompeuse : elle laisse créer un Store qui n'ingère rien. Soit on livre l'adaptateur, soit on retire l'option du dropdown jusqu'à ce qu'elle soit fonctionnelle. À programmer pour 0.7.0 (pas urgent — pas de régression sur l'existant).
References
document-parser/infra/neo4j.py— driver async existantdocument-parser/infra/opensearch_store.py— portVectorStoreà imiter