> ## Documentation Index
> Fetch the complete documentation index at: https://docs.canary.effectiveai.app/llms.txt
> Use this file to discover all available pages before exploring further.

# relativities.ppa.vin

> Every public name in relativities.ppa.vin, from its docstrings.

*Generated from the package's Python API.*

Decode vehicle identification numbers into make, model, year and build facts.

`VinDecoder.load()` opens the vPIC database this release of Relativities
pins: NHTSA's Product Information Catalog, as packaged by `@cardog/corgi
&lt;https://github.com/cardog-ai/corgi&gt;`\_. The first load downloads it (about 26 MB)
into the cache, checks its SHA-256, and every later load reads it offline.
Decoding runs locally, in Python, and gives the same answer for the same VIN
on every machine for one release.

::

from relativities.ppa import vin

with vin.VinDecoder.load() as decoder:
decoder.decode(\["1HGCV1F31LA123456"])  # make Honda, model Accord, model\_year 2020
book = decoder.enrich(book)  # fills blank vehicle facts from each VIN

`enrich` takes a book and returns one, so it is a `@scenario` or a step
before `Program.rate`. `vin.COLUMNS` declares the facts it writes, for
`Schema.with_columns`.

## COLUMNS

```python theme={null}
COLUMNS: mappingproxy
```

The vehicle facts a VIN decodes to, as `Column`s by name: `make`, `model`, `model_year`, and build facts such as `body_class`, `drive_type`, `fuel_type` and `engine_cylinders`. `VinDecoder.decode` returns one column for each, and `VinDecoder.enrich` writes them. Declare the ones you keep with `Schema.with_columns`, leaving out `make`, `model` and `model_year`, which `ppa.SCHEMA` already declares.

## VPIC

```python theme={null}
VPIC: Resource
```

The vPIC database this release pins: its release name, the URL it downloads from, and the SHA-256 of the archive and of the database file. `VinDecoder.load()` reads it.

## VinDecoder

```python theme={null}
class VinDecoder()
```

A vPIC database, opened read-only, that decodes VINs.

`load` opens it. Close it with `close` or a `with` block. Decoding
reads only the database, so a decoder gives one fixed answer for each VIN.

### VinDecoder.load

```python theme={null}
@classmethod
def load(
    cls,
    path: str | Path | None = None,
    *,
    local_files_only: bool = False,
    cache_dir: str | Path | None = None,
) -> VinDecoder
```

Open the pinned vPIC database, or a vPIC SQLite file at `path`.

By default, the first load downloads `VPIC` from its pinned URL,
anonymously, checks its SHA-256 and caches it; later loads check the
cached copy and open it offline. `local_files_only`, or
`EFFECTIVE_OFFLINE=1`, reads only the cache. A failed download, a
cache that lacks the file offline, or a cached file that fails its
check raises `relativities.CatalogError`. `cache_dir` sets the
cache directory.

`path` opens a vPIC database you have, such as a newer corgi
`vpic.lite.db`, as it is.

### VinDecoder.download

```python theme={null}
@staticmethod
def download(
    path: str | Path,
    *,
    local_files_only: bool = False,
    cache_dir: str | Path | None = None,
) -> Path
```

Copy the pinned vPIC database to `path`, a new file, and return it.

`VinDecoder.load(path)` then opens it on an offline machine.
`local_files_only` and `cache_dir` work as in `load`. Raises
`FileExistsError` when `path` exists.

### VinDecoder.release

```python theme={null}
@property
def release(self) -> str
```

The data this decoder reads, such as `NHTSA vPIC 2026-04-01 (@cardog/corgi 2.0.4)`.

### VinDecoder.decode

```python theme={null}
def decode(self, vins: Iterable[Any]) -> pd.DataFrame
```

One row per VIN, in order: what the VIN says about the vehicle.

`vin` is the value given. `status` is `decoded`; `invalid`
(something other than 17 VIN characters); `unknown_wmi` (a
manufacturer code outside vPIC); `unknown_year` (vPIC lacks the
model year position 10 encodes); or `no_match` (a known
manufacturer, with every pattern missing). `check_digit_ok` says
whether position 9 holds the check digit. Then each fact in
`COLUMNS`, blank where the VIN is silent. Decoding ignores case and
surrounding space.

### VinDecoder.enrich

```python theme={null}
def enrich(
    self,
    book: Book,
    *,
    table: str = 'vehicles',
    vin: str = 'vin',
    columns: Iterable[str] | None = None,
) -> Book
```

The book with each blank vehicle fact filled from the row's VIN.

`columns` names the facts to write, by default every one in
`COLUMNS`; `ppa.SCHEMA` already declares `make`, `model` and
`model_year`. A fact the book holds stays as given: only blank cells
change, so reported data always wins. A fact the table
lacks becomes a new column, typed when the schema declares it (see
`COLUMNS`). A row whose VIN fails to decode, with any status other than
`decoded`, stays as it is.

As a study scenario::

@scenario("Decoded VINs")
def decoded(book):
return decoder.enrich(book)

### VinDecoder.close

```python theme={null}
def close(self) -> None
```

Close the database. Load a decoder again to decode after closing.

## check\_digit

```python theme={null}
def check_digit(vin: str) -> str
```

The check digit (position 9) that a VIN's other positions require.

```python theme={null}
vin.check_digit("1HGCV1F31LA123456")
```

'1'

Raises `ValueError` unless `vin` is 17 VIN characters: letters other than
I, O and Q, and digits.

## is\_valid

```python theme={null}
def is_valid(vin: Any) -> bool
```

Whether `vin` is 17 VIN characters with the check digit they require.

North American VINs carry a check digit. Many others use position 9 for
something else, and still decode.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.