Skip to main content

Thisness

Identity resolution for imperfect observations.

For centuries, Hesperus (the evening star) and Phosphorus (the morning star) were two things. Then observation showed they were the same planet. Two observations, from different times and contexts, of one underlying thing, and the identity established by evidence rather than by definition.

Thisness does that for data. It takes observations from many sources ("IBM", "IBM Corp.", "International Business Machines Corporation", an invoice line, a contract party) and decides what they refer to, keeping the evidence, the decision, and the history behind every conclusion, so any of them can be explained, reviewed, or reversed.

Status: alpha. Three domains resolve end to end: organizations, people, and taxa (scientific plant names). Each has a review queue, dossiers, a local Workbench, and an optional language model that adjudicates contested pairs or suggests review answers. The API will change. The design is in docs/design/.

pip install 'thisness[ui]'
import csv
from thisness import Thisness
from thisness.profiles.organization import organization

engine = Thisness("identity.duckdb", profiles=[organization()])

# Columns such as name, website, email, registration_ids, country, city, postal_code.
with open("vendors.csv", newline="") as f:
    engine.ingest(
        [
            engine.observe(
                "organization", row, {"type": "csv", "id": "vendors.csv", "locator": {"row": i}}
            )
            for i, row in enumerate(csv.DictReader(f))
        ]
    )

result = engine.resolve_many("organization")  # fits a model on first use, then resolves
print(result.stats)  # pairs scored, clusters, reviews opened

# Later records resolve against what already exists.
outcome = engine.resolve(
    "organization",
    {"name": "IBM Corp.", "website": "https://www.ibm.com"},
    {"type": "crm", "id": "account-17"},
)
print(outcome.outcome, outcome.entity_id)  # ATTACH_TO_EXISTING / CREATE_ENTITY / REQUIRES_REVIEW
engine.explain(outcome.decision_id)  # the decision, its evidence, the pairs behind it

A store needs a few hundred records of a type to fit its own model. Below that, Thisness uses a shipped starter model, and replaces it with a fitted one on the first run with enough data.

Every decision is stored with its evidence; merges and splits are reversible ledger events (engine.merge_entities, engine.split_entity, engine.history).

Domains

A domain is a resolution profile: its attributes, normalizers, comparisons, constraints and the guidance a model is given. Three ship.

  • Organizations (thisness.profiles.organization): names with legal forms, abbreviations, acronyms and group-company qualifiers; websites, emails, registration IDs, addresses. A shared brand or a group's sibling companies are relationship candidates, never merges.
  • People (thisness.profiles.person): names with nicknames, initials and spelling variants; dates of birth, emails, phones, addresses. Same name at the same address (a household, a parent and child) stays a relationship until evidence says otherwise.
  • Taxa (thisness.profiles.taxon): scientific names parsed into genus, epithets, rank and authorship. Synonyms join their accepted taxon when an authority such as World Flora Online or the Catalogue of Life is loaded, and every label says which authority supplied it.

From the command line

thisness ingest vendors.csv --resolve   # into identity.duckdb
thisness ui                              # the Workbench: entities, reviews, history, context
thisness --store demo.duckdb demo && thisness ui demo.duckdb    # try it on synthetic data
thisness --store people.duckdb demo --type person                # synthetic people
thisness --store taxa.duckdb demo --type taxon --reference       # real plant names, labelled by WFO

The Workbench is a local web app over the same API: browse entities with the evidence for every value, answer the review queue by dragging records between entities, relate or merge entities that share a brand, page through possible relationships, and see exactly what a language model was given and what it answered.

With a language model

A model is optional, and the engine never needs one. Where it helps, it is given the contested pairs or the open reviews, with the evidence for each, and its answers are recorded like any other decision.

pip install 'thisness[ui,openai]'   # or [ui,anthropic]; keys from OPENAI_API_KEY / ANTHROPIC_API_KEY

thisness resolve --provider openai --model gpt-6-luna --budget 2   # contested pairs to the model
thisness recommend --provider openai --model gpt-6-luna --limit 50 # suggested answers for reviews
thisness ui --provider openai --model gpt-6-luna                   # ask from the Workbench

What the model does is set per profile (model_role): for organizations and taxa it decides contested pairs, and its "same" yields to its own "different" or to a cannot-link constraint; for people it only suggests review answers, since measured on people it was both too cautious and too credulous. --model-role decide|suggest|shadow|off overrides that for a run. --record keeps every exchange in a cassette and --replay answers from one, free and offline. Any OpenAI-compatible server works with --base-url (and --local when it is yours).

Set --budget: there is no default limit.

How well it works

thisness eval resolves a benchmark with known truth and reports precision, recall and false-merge clusters. Measured so far (design 07 §13; design 06 §10):

Benchmark Records P R False-merge clusters
Real UK companies (GLEIF + Companies House), identifiers hidden 3,507 .9992 .912 1
The same, gpt-6-luna deciding contested pairs ($0.62) 3,507 .9965 .938 5
Real UK companies, at scale 46,565 .9965 .934 63
Synthetic organizations, 4,000 .9995 .938 4
thisness eval --size 805 4000                     # synthetic organizations (or --type person)
thisness eval --type taxon --reference            # WFO plant names, with WFO's own rows
python -m thisness.benchmark uk-stage             # download GLEIF and Companies House (~1 GB, once)
thisness eval --source uk --size 400              # real UK companies, identifiers hidden
thisness eval --source uk --adjudicator openai --model gpt-6-luna --mode decide --budget 2

The UK benchmark's hard cases are real: 62% of live UK companies share a registered address, and a record that says only "Sports Direct" sits beside four real Sports Direct companies. The record variations (shortened names, typos, partial addresses) are generated from the true company, so the truth is known.

Known limits

  • Memory grows with candidate pairs. 46,565 records took 7.5 minutes of CPU and 7.8 GB peak memory; scoring holds every candidate pair in Python. Tested to about fifty thousand records.
  • Candidates come from blocking keys. There is no embedding-based candidate generator yet, so two records that share no blocking key (a renamed company with nothing else in common, a heterotypic synonym with no authority loaded) are never compared directly.
  • Model costs are real. About $0.62 for 400 UK companies with gpt-6-luna; the default model is claude-sonnet-5. Use --budget, and --replay to re-run without paying again.
  • The network benchmarks download data. WFO families beyond the shipped three, the Catalogue of Life, and the UK sources are fetched on demand and cached under ~/.cache/thisness. Everything else, including the Workbench, runs offline.

What it is

  • Observations are not entities. A source record is an observation of something. Entities are stable identities above them, with IDs that never change when names do.
  • Similarity is not identity. Candidate retrieval, evidence, decisions and identity state are separate steps with separate records. Scores are typed; there is no universal confidence float.
  • Identity is not relationship. "Subsidiary of" and "brand of" are relationships between distinct entities, never merges.
  • Everything is reversible. Merges and splits are ledger events over memberships. Nothing is destructively collapsed, and the ledger alone can rebuild current state.
  • Models propose; policy decides. Rules, Fellegi-Sunter, LLMs and people all contribute decisions. Only a commit, citing those decisions, changes identity state.

Documents

Development

uv sync --dev   # includes the ui extra
uv run pytest
uv run ruff check . && uv run ruff format --check .

Releases publish to PyPI from a version tag: bump version in pyproject.toml, add a CHANGELOG.md entry, then git tag vX.Y.Z && git push origin vX.Y.Z.

Data and license

Thisness is Apache-2.0. See LICENSE.

Bundled and fetched data keep their own terms, credited where they ship: World Flora Online (CC0 1.0) in benchmark/data; GLEIF Golden Copy (CC0 1.0) and Companies House data (Open Government Licence v3.0) in benchmark/data/uk; the Catalogue of Life (CC BY 4.0), fetched on demand with attribution; and the Workbench's fonts, icons and scripts in ui/static/THIRD_PARTY.md.

Metadata

Release files for thisness 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for thisness 0.1.0
File Size Uploaded
thisness-0.1.0.tar.gz 819.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for thisness 0.1.0
File Interpreter ABI Platform
thisness-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.4 MB

Release files / thisness-0.1.0.tar.gz

Download URL thisness-0.1.0.tar.gz
Size 819.8 kB
Tags Source
SHA-256 checksum
How to use checksums
53ed85e817f29442cd0270d4d64faefe40995fc0c234b7aef649ab01c3871b4e
BLAKE2b-256 checksum
How to use checksums
6e119388215c4278a1643c944b2c8181091116ffadaffb6ca9032f3dcc2acbad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / thisness-0.1.0-py3-none-any.whl

Download URL thisness-0.1.0-py3-none-any.whl
Size 607.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c653863fc152e18476785da502d3653a139ac54509b5b04765e15a1cd609d6e4
BLAKE2b-256 checksum
How to use checksums
178f95a3f541dc74e133538dadd46c89a451fcf3be644b66a656af081a094240
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page