Extraction Pipeline
How DocuStore extracts chemical structures (OCSR), named entities (NER), and summaries from scientific documents.
When a document is uploaded to DocuStore, it enters an automated extraction pipeline that identifies chemical structures, named entities, and generates summaries. Each extraction stage runs as an independent Temporal workflow triggered by domain events.
Chemical Structure Recognition (OCSR)
The compound extraction pipeline uses the structflo-cser ML library to identify chemical structure diagrams in PDF pages and extract their SMILES representations.
How It Works
- Trigger -- A
Page.Createdevent fires when a new page is added to an artifact. - Image rendering -- The PDF page is rendered to a high-resolution image (2x zoom via PyMuPDF).
- Detection -- A YOLO object detector identifies structure diagram regions and their associated text labels.
- Matching -- A neural matcher pairs each structure diagram with its label.
- SMILES extraction -- DECIMER converts each detected structure into a SMILES string.
- Persistence -- Valid results are stored as
CompoundMentionvalue objects on the Page aggregate.
Page.Created → ExtractCompoundMentionsWorkflow
→ YOLO detection → Neural matching → DECIMER SMILES
→ Page.CompoundMentionsUpdated
→ SmilesEmbeddingWorkflow (ChemBERTa indexing)Each CompoundMention records:
| Field | Description |
|---|---|
smiles | Canonical SMILES string |
extracted_id | Label text from the document (e.g., "SACC-111") |
confidence | Match confidence score |
model_name | Always structflo-cser |
date_extracted | UTC timestamp |
Pairs without a valid SMILES string are silently dropped. The workflow ID compound-extraction-{page_id} ensures idempotency.
Manual Trigger
curl -X POST http://localhost:8000/pages/{page_id}/compounds/extractReturns 202 Accepted with the workflow ID. Useful for re-running extraction after corrections.
Named Entity Recognition (NER)
The NER pipeline extracts typed entities from page text using structflo-ner, an LLM-powered extraction library tuned for pharmaceutical and biological domains.
Entity Types
| Type | Examples |
|---|---|
compound | SACC-123, Rifampicin, ethionamide |
target | NadD, InhA, KasA |
disease | tuberculosis, TB, MDR-TB |
gene | Rv3138, nadD |
protein | NaMN adenylyltransferase, EthA |
assay | MIC, MABA, LORA |
bioactivity | IC50=2.3uM, MIC90=0.5ug/mL |
mechanism | competitive inhibition, covalent binding |
accession | P0A7D9, 3Q5Z, CHEMBL12345 |
Pipeline Flow
NER runs in parallel with text embedding, both triggered by Page.TextMentionUpdated. They write to different fields on the Page aggregate, so there are no concurrency conflicts.
Page.TextMentionUpdated
├─ TextEmbeddingWorkflow (writes text_embedding_metadata)
└─ NERExtractionWorkflow (writes tag_mentions)
└─ Page.TagMentionsUpdated
└─ ArtifactTagAggregationWorkflowAfter NER completes for a page, the ArtifactTagAggregationWorkflow collects all tags from all pages of the parent artifact, deduplicates them, and stores the aggregated set on the Artifact. This propagation is eventually consistent -- each new page's NER results refresh the artifact-level tags.
Configuration
| Variable | Default | Description |
|---|---|---|
NER_MAX_CHAR_BUFFER | 5000 | Max characters per LLM chunk in NER extraction |
GLINER2_MODEL_NAME | fastino/gliner2-large-v1 | GLiNER2 model for structured metadata extraction |
LLM Summarization
Summarization operates at two granularities:
Page Summaries
Each page is summarized individually by an LLM after its text embedding is generated. The sequential dependency (TextMentionUpdated -> embedding -> TextEmbeddingGenerated -> summarization) prevents concurrency conflicts that would occur if both ran in parallel on the same aggregate.
Artifact Summaries
Once all pages in an artifact have summaries, a sliding-window summarization chain produces a document-level summary:
- Batch -- Page summaries are grouped into batches of
ARTIFACT_SUMMARIZATION_BATCH_SIZE(default 10). - Synthesize -- Each batch is summarized into a batch summary.
- Refine -- Batch summaries are iteratively refined into a single coherent document summary.
Prompts for each stage are managed in Langfuse: artifact_batch_summary, artifact_synthesis, artifact_refinement.
Summary Locking
Summaries support a is_locked flag. When a human corrects a summary and locks it, automated re-summarization preserves the locked version. The hil_correction field tracks the human-edited text alongside the original.
Configuration
| Variable | Default | Description |
|---|---|---|
LLM_PROVIDER | ollama | LLM provider (ollama, openai, gemini) |
LLM_MODEL_NAME | gemma3:27b | Model for summarization |
LLM_TEMPERATURE | 0.1 | Low temperature for deterministic summaries |
ARTIFACT_SUMMARIZATION_BATCH_SIZE | 10 | Pages per batch in artifact summarization |