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 an image at 150 DPI by structflo's
render_page. The render is stored so that bounding boxes always refer to the image the model saw. - Detection -- A D-FINE object detector identifies structure diagram regions and their associated text labels.
- Matching -- The RelationalMatcher pairs each structure diagram with its label. It uses geometry only, reasoning over all detections on the page jointly with an optimal-transport assignment that can leave a structure or label unmatched.
- SMILES extraction -- DECIMER converts each detected structure into a SMILES string; OCR reads the label text.
- Persistence -- Valid results are stored as
CompoundMentionvalue objects on the Page aggregate.
Page.Created → ExtractCompoundMentionsWorkflow
→ D-FINE detection → RelationalMatcher → DECIMER SMILES + label OCR
→ Page.CompoundMentionsUpdated
├─ EmbedCompoundSmilesWorkflow (ChemBERTa indexing)
└─ ReconcileCompoundLabelsWorkflow (labels checked against NER compound names)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
Each extracted entity is stored as a tag with one of these entity_type values:
| Type | Examples |
|---|---|
compound_name | SACC-123, Rifampicin, ethionamide |
smiles | CC(=O)Oc1ccccc1C(=O)O |
cas_number | 50-78-2 |
molecular_formula | C9H8O4 |
target | NadD, InhA, KasA |
gene_name | Rv3138, nadD |
protein_name | NaMN adenylyltransferase, EthA |
disease | tuberculosis, TB, MDR-TB |
bioactivity | IC50=2.3uM, MIC90=0.5ug/mL |
assay | MIC, MABA, LORA |
mechanism_of_action | competitive inhibition, covalent binding |
accession_number | P0A7D9, 3Q5Z, CHEMBL12345 |
product | ATP synthase subunit c, decaprenylphosphoryl-beta-D-ribose 2-epimerase |
functional_category | cell wall, lipid metabolism |
screening_method | fragment, biochemical, DEL, hypomorph |
Some entities carry attributes as well. For example, a bioactivity or assay entity can record the strain the value was measured against (such as H37Rv).
Pipeline Flow
NER runs in parallel with page summarization and document metadata extraction, all triggered by Page.TextMentionUpdated. They write to different fields, so they do not conflict.
Page.TextMentionUpdated
├─ PageSummarizationWorkflow (writes summary_candidate)
├─ NERExtractionWorkflow (writes tag_mentions)
│ └─ Page.TagMentionsUpdated
│ ├─ ArtifactTagAggregationWorkflow
│ └─ ReconcileCompoundLabelsWorkflow
└─ DocumentMetadataExtractionWorkflow (first page; writes title, authors, date on the Artifact)Each page runs a fast dictionary-based extractor (for targets, gene names, accession numbers and screening methods) alongside the LLM extractor and merges the results; LLM results take precedence where both find the same entity.
After 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 |
NER_LLM_PROVIDER | Falls back to LLM_PROVIDER | LLM provider for NER (ollama, openai, gemini); other providers fall back to dictionary-only NER |
NER_LLM_MODEL_NAME | Falls back to LLM_MODEL_NAME | Model for NER |
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 as soon as its text is extracted (Page.TextMentionUpdated). Each summary is embedded into summary_embeddings as it arrives. Once every page has a summary, the artifact's text chunks are embedded in a single batch (BatchReEmbedArtifactPagesWorkflow), so that each chunk's context header includes its page summary.
Artifact Summaries
Once all pages in an artifact have summaries, ArtifactSummarizationWorkflow runs a sliding-window summarization chain that 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, anthropic, gemini) |
LLM_MODEL_NAME | gemma4:31b | Model for summarization |
LLM_TEMPERATURE | 0.1 | Low temperature for deterministic summaries |
ARTIFACT_SUMMARIZATION_BATCH_SIZE | 10 | Pages per batch in artifact summarization |