Skip to main content

Shared FOLIO source-text-to-concept resolution engine: scorer, span decomposition, gates, alias blocklist, domain-prior judge, and annotate primitives.

Project description

folio-resolve

The shared FOLIO source-text→concept matching engine. One pinned, MIT-licensed Python library that maps arbitrary source text (book headings, deposition transcripts, intake narratives, task titles) to concepts in FOLIO — the open legal ontology of 18,000+ concepts.

This is a lift-and-improve extraction. The matching intelligence already existed across folio-mapper, folio-enrich, and folio-insights — but it had diverged (enrich literally forked mapper's scorer) and was informally shared through a fragile sys.path hack. folio-resolve is the single source of truth those repos now pin, plus the capabilities the recorded failures demanded.


Why this exists

Three repos independently built the same engine and drifted apart:

  • folio-mapper wrote the canonical, word-order-invariant scorer + a 4-stage pipeline + a FAISS embedding index.
  • folio-enrich copied mapper's scorer (its search.py says "ported from folio-mapper" in the code) and added an Aho-Corasick entity ruler, a reconciler, a domain-prior judge, and a feature-rich annotate/feedback UI.
  • folio-insights sys.path-imports both siblings at runtime — documented as fragile.

A book-annotation review (Ch02) then surfaced failures that no amount of local patching would fix: place-names over-scoring (Slovenia → 99 units), homonyms (Action ≠ Auction), conjoined compound headings that match nothing, metadata tagged as substance, and pure-semantic maps with no shared label token (Presumptions → Litigation Burdens of Proof). Those became this library's new capabilities.

What it does

Capability Module Solves
Word-order-invariant relevance scoring scoring "arbitration rules" = "rules of arbitration"
Multi-strategy label search + legal expansions scoring, pipeline recall on paraphrases
Pure-Python Aho-Corasick entity ruler entity_ruler, matching trusted exact-label spans, no spaCy/C deps
Candidate reconciliation (ruler + LLM + semantic) reconciler one clean candidate set with provenance
Span decomposition (conjunction split + shared head) decompose "Findings of Fact and Conclusions of Law" → both siblings
Place-name / short-label gates gates kills Slovenia→99 and Presumptions→Northern Mariana Islands@90
Alias/homonym blocklist blocklist deterministic Action ≠ Auction guard
Metadata/front-matter exclusion sources never tag the copyright page
Multi-tag domain prior (auto-suggest + validate/add) domain_prior Defenses → Litigation Defenses; corpus carries many subjects
Score calibration (weak-band recalibration) calibration verdict-labeled score→P(correct) fit
LLM judge interface + domain-prior prompts judge context-aware disambiguation, verdict enforcement
Annotate primitives (confidence, per-tag verdicts, notes, reject/restore, insights) annotate the self-improving feedback loop, as a library
4-stage pipeline: filter → expand → rank → judge pipeline the build-once-use-many entry point

Personas

  • Book-extraction pipelines (folio-insights, books) — offline, CLI-first tagging of treatise text.
  • Intake / matter classifiers (alea-intake, mootloop) — narrative → FOLIO concepts for routing.
  • Interactive mappers (folio-mapper, clio-skills) — a UI over the library's ranked candidates.
  • The FOLIO ontology team — a reference implementation of "good" source-text→concept matching.

Use cases

  • Tag a chapter of a litigation treatise with FOLIO concepts, with a litigation domain prior so "Defenses" resolves to Litigation Defenses, not the generic sense.
  • Resolve a compound heading like "Proposed Findings of Fact and Conclusions of Law" to the two sibling concepts it actually names.
  • Suggest the subject tags for a corpus ("Personal Injury Depositions" → Personal Injury + Deposition) and let a human validate/add via a FOLIO taxonomy-tree picker.
  • Feed a reviewer's wrong verdict on a homonym straight into the alias blocklist so the mistake never recurs — the self-improving loop.

Install

uv add folio-resolve                 # core (pure-Python, only pydantic)
uv add "folio-resolve[folio]"        # + folio-python live ontology adapter
uv add "folio-resolve[embedding]"    # + sentence-transformers / faiss for the semantic path
uv add "folio-resolve[spacy]"        # + optional spaCy ruler adapter

The core is pure-Python — the scorer, decomposition, gates, blocklist, domain-prior, reconciler, calibration, and annotate primitives depend on nothing heavier than pydantic. FAISS, sentence-transformers, spaCy, and folio-python live behind Protocol seams with working pure-Python defaults, so the whole test suite runs with no model downloads and no network.

Bring Your Own Key (BYOK)

folio-resolve is key-agnostic — it never reads an env var, instantiates a provider SDK, or makes a network call on its own. The zero-key deterministic core (ruler, scoring, decomposition, gates, blocklist, metadata exclusion, calibration, annotate) runs fully offline and free. Three optional stages accept a provider through a typing.Protocol seam you fill with an object you construct:

Stage Protocol Buys you Absent →
Judge Judge.complete(system, user) -> str context-aware disambiguation + verdict enforcement items pass through unjudged
Embeddings EmbeddingProvider (embed/embed_batch/dimension) semantic recall for no-shared-token maps local all-MiniLM-L6-v2 default (no key), or skipped
Domain-prior suggestions DomainPriorSuggester(ontology) auto-suggest corpus subject tags supply tags manually

You own the key, the vendor (OpenAI / Gemini / Anthropic / local), and the spend. The library ships the judge prompt builders and deterministic verdict enforcement; you supply only the raw model call. Graceful degradation is the default — no key means deterministic-only output with items marked unjudged, never a crash. Reference cost: ≈ $0.12 / chapter on gemini-2.5-flash-lite (~1,875 calls, ~652K tokens, 464 units). Full guide, env-var conventions, and a minimal wiring example per vendor: docs/BYOK.md.

Quick start

from folio_resolve import InMemoryOntology, Concept, MatchPipeline, DomainPrior

ontology = InMemoryOntology([
    Concept(iri="R-defenses", label="Litigation Defenses", branch="Objectives"),
    Concept(iri="R-arb", label="Arbitration Rules", branch="Service"),
])
pipe = MatchPipeline(ontology=ontology)

# word-order-invariant
pipe.match("rules of arbitration")        # -> Arbitration Rules

# domain prior threads a subject into the (optional) judge
prior = DomainPrior.from_manifest_subjects("treatise", [("R-lit", "Litigation")])
pipe.match("Defenses", domain_prior=prior)

Development

uv sync --extra dev
uv run pytest          # full suite, pure-Python, no network
uv run mypy src        # strict
uv run ruff check

License & attribution

MIT — see LICENSE. Every extracted component and dependency is logged in THIRD-PARTY.md. The migration schedule for consumer repos is in docs/migration/SCHEDULE.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

folio_resolve-0.1.0.tar.gz (59.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

folio_resolve-0.1.0-py3-none-any.whl (50.4 kB view details)

Uploaded Python 3

File details

Details for the file folio_resolve-0.1.0.tar.gz.

File metadata

  • Download URL: folio_resolve-0.1.0.tar.gz
  • Upload date:
  • Size: 59.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for folio_resolve-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e20f856586bc8c01b7dbd75fd972c77b8b02d9eda3ab40c9e7c7e76d1c741475
MD5 4e7247bc3ac89f43915ef5db380122e3
BLAKE2b-256 446abf8ae9f70743936ef96cd5b860b14ff585a58814b4ff5ac725a9f90592d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for folio_resolve-0.1.0.tar.gz:

Publisher: publish.yml on damienriehl/folio-resolve

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file folio_resolve-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: folio_resolve-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 50.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for folio_resolve-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5da48dc74ba336754febd4a60bdfa4e65902e90758268c38d58452f2c8cb9fa5
MD5 e73bbf7934ccec8f3188ffc2653d9b0c
BLAKE2b-256 bc35e5a2b9a211250e0bdef756b4c8682526551af88d90981600f9b7e37cba4f

See more details on using hashes here.

Provenance

The following attestation bundles were made for folio_resolve-0.1.0-py3-none-any.whl:

Publisher: publish.yml on damienriehl/folio-resolve

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page