DocuStore.io
Architecture

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

  1. Trigger -- A Page.Created event fires when a new page is added to an artifact.
  2. 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.
  3. Detection -- A D-FINE object detector identifies structure diagram regions and their associated text labels.
  4. 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.
  5. SMILES extraction -- DECIMER converts each detected structure into a SMILES string; OCR reads the label text.
  6. Persistence -- Valid results are stored as CompoundMention value 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:

FieldDescription
smilesCanonical SMILES string
extracted_idLabel text from the document (e.g., "SACC-111")
confidenceMatch confidence score
model_nameAlways structflo-cser
date_extractedUTC 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/extract

Returns 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:

TypeExamples
compound_nameSACC-123, Rifampicin, ethionamide
smilesCC(=O)Oc1ccccc1C(=O)O
cas_number50-78-2
molecular_formulaC9H8O4
targetNadD, InhA, KasA
gene_nameRv3138, nadD
protein_nameNaMN adenylyltransferase, EthA
diseasetuberculosis, TB, MDR-TB
bioactivityIC50=2.3uM, MIC90=0.5ug/mL
assayMIC, MABA, LORA
mechanism_of_actioncompetitive inhibition, covalent binding
accession_numberP0A7D9, 3Q5Z, CHEMBL12345
productATP synthase subunit c, decaprenylphosphoryl-beta-D-ribose 2-epimerase
functional_categorycell wall, lipid metabolism
screening_methodfragment, 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

VariableDefaultDescription
NER_MAX_CHAR_BUFFER5000Max characters per LLM chunk in NER extraction
NER_LLM_PROVIDERFalls back to LLM_PROVIDERLLM provider for NER (ollama, openai, gemini); other providers fall back to dictionary-only NER
NER_LLM_MODEL_NAMEFalls back to LLM_MODEL_NAMEModel for NER
GLINER2_MODEL_NAMEfastino/gliner2-large-v1GLiNER2 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:

  1. Batch -- Page summaries are grouped into batches of ARTIFACT_SUMMARIZATION_BATCH_SIZE (default 10).
  2. Synthesize -- Each batch is summarized into a batch summary.
  3. 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

VariableDefaultDescription
LLM_PROVIDERollamaLLM provider (ollama, openai, anthropic, gemini)
LLM_MODEL_NAMEgemma4:31bModel for summarization
LLM_TEMPERATURE0.1Low temperature for deterministic summaries
ARTIFACT_SUMMARIZATION_BATCH_SIZE10Pages per batch in artifact summarization