Detection
Before Soria can extract data from a PDF, it needs to know which pages contain the data you care about. A report might be 80 pages long, but only pages 12–15 contain the enrollment table you want. Detection uses Gemini to scan each file and identify the pages (for PDFs) or sheets (for Excel) that contain data matching your group’s schema columns. Those pages are collected into a sub-file that carries only the relevant content forward to extraction. For PDFs, detection works on child groups. You create a child group under the parent group that holds the raw PDFs, and detection writes the detected sub-PDFs into that child group. This lets you extract multiple different data types from the same set of PDFs — one child group per data type.force=True to re-detect.
You must define schema columns on the group before running detection. The schema tells Gemini what kind of data to look for.
Extraction
Once detection has identified the relevant pages, extraction uses Gemini to pull the tabular data out of those pages and write it into a CSV file. The output CSV uses your schema columns as headers. Because the schema is defined upfront, there’s no schema drift at extraction time — the columns are always canonical.force=True to re-extract.
Testing extraction
Before running extraction on all files, you can test your extraction setup against a single file:Excel and CSV extractors
For Excel and CSV files, extraction uses a Python class called aSimpleExtractor rather than AI. You implement an extract(reader) generator method that yields DataFrames in chunks.
reader provides two methods:
reader.read(n_rows=N)— read the first N rows as a raw DataFrame (no headers, all strings), useful for peeking at structurereader.iter(start_row=N, columns=[...], chunk_size=M)— yield DataFrames in chunks starting at a given row
extractor_manage. Only polars is available for DataFrames — pandas is not installed.
Validation
After extraction, validation cross-checks the extracted CSV against the source document. It uses a combination of heuristics and Gemini to verify that the data looks correct and applies corrections where possible. Validation runs automatically after extraction. You can also run it manually — for example, to re-validate after fixing an extraction issue:force=True to re-validate files that already have results.
Value mapping
When AI extracts data, it can produce slightly inconsistent values for the same underlying concept. For example, an unlabeled “total” row in a table might be extracted as"total", "overall", or "all" depending on the context. These are all the same thing, but they’d appear as three separate values in your warehouse.
Value mapping solves this. It has two steps:
1. Index values — scan all extracted CSVs for a given column and collect every distinct value found.
- Canonicals — normalized forms that other values will be mapped to
- Unmapped — values not yet assigned to a canonical, with similarity suggestions
- Mapped — values that have been normalized, showing the canonical they map to
Schema drift for CSVs
CSVs skip detection, extraction, and validation entirely. But they have their own challenge: schema drift — column names that change across files or over time. For example, a state Medicaid enrollment file might use"Plan Name" in 2022 and "MCO Name" in 2024. Both mean the same thing, but they’d land in different columns without schema mappings.
schema_mappings handles this by mapping source CSV headers to canonical schema columns. Soria suggests matches automatically based on name similarity. You confirm the suggestions and manually map anything it can’t match.
scraper_run reports it as schema drift so you can map it before publishing.