Metadata-Version: 2.4
Name: fihrist
Version: 0.1.0
Summary: Place a book on a culturally-parity-audited lattice of 3,660 leaves across 10 axes.
Project-URL: Homepage, https://github.com/bk2022-ui/lattice
Project-URL: Source, https://github.com/bk2022-ui/lattice
Project-URL: Issues, https://github.com/bk2022-ui/lattice/issues
Author: Bharat Khandelwal
License: Apache-2.0
License-File: LICENSE
License-File: LICENSE-DATA
Keywords: bias,books,classification,faceted-classification,libraries,llm,taxonomy
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Sociology :: History
Classifier: Topic :: Text Processing :: Indexing
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# Fihrist

**Place a book on a lattice that stays even-handed when you change the model underneath it.**

```bash
pip install fihrist
```

Fihrist ships a faceted classification artifact — **3,660 leaves across 10 axes** — built so that no
tradition sits at the centre as the unmarked default. Give it a book's *contents* and it tells you
where that book belongs. The placement is earned from what the work is about, never from the
author's nationality or the country it was published in.

Named for the *Kitāb al-Fihrist* (Baghdad, 987 CE), a bookseller's attempt to index every book then
known, across every language he could reach.

---

## What you need before you start

**Fihrist takes an enriched book record, not a title.**

A title and an author are not enough, and that is the whole point rather than a missing feature. The
lattice slots on what a book *contains*. Feed it a bare title and the only thing a model can classify
on is the author's name and origin — which is precisely the bias the artifact exists to remove.

So the input is a record carrying **idea units**: the handful of things the book is actually about.
Step 2 below shows how to get them.

## What Fihrist does and does not do

Fihrist makes **no network calls** and **never sees an API key**. It builds prompts and checks
answers. The model call is yours, on your account, with your choice of provider.

| Fihrist owns | You own |
|---|---|
| the artifact, verified against its sha256 on load | the API key |
| leaf and parent lookup, the 9 embedded parity audits | the model call |
| building the research prompt | running it |
| building the slot prompt | running it |
| validating the reply — invented leaves are dropped | |

Reads as a limitation for about ten seconds, and as a feature forever: no key, no telemetry, no
surprise bill, and it works on a plane.

---

## 1 — Inspect the artifact

Offline, immediate, free.

```python
import fihrist

lat = fihrist.load()              # hash-verified against the shipped sha256
len(lat)                          # 3660
lat.axes                          # ['Discipline', 'Place', 'Time', 'Form', 'Genre', ...]
len(lat.leaves("Discipline"))     # 1246

lat.find("Meiji")                 # [Leaf('Modern Japan (Meiji–Shōwa)', axis='Discipline', ...)]
lat.parents_of("Modern Japan (Meiji–Shōwa)", "Discipline")
# ['Japanese history']

lat.audits                        # the 9 parity audit reports, carried inside the file
```

The artifact carries its own audits. You do not have to take the parity claim on trust — open
`lat.audits` and read what was checked.

From the shell:

```bash
fihrist verify
fihrist axes
fihrist leaves --axis Discipline --grep literature
```

## 2 — Enrich a book

Fihrist writes the research prompt. You run it against any model that can search the web.

```python
prompt = fihrist.enrich_prompt("I Am a Cat", "Natsume Sōseki")
record = fihrist.parse_enrichment(your_model(prompt))

record["ideaUnits"]
# [{'id': 'the-outsider-narrator-sees-clearest-1',
#   'title': 'The Outsider Narrator Sees Clearest',
#   'tags': ['narration', 'satire', 'detachment']}, ...]
```

The instruction body is frozen and byte-identical for every book — only the assignment block at the
end changes. That constancy is deliberate. A prompt that said more about a Japanese novel than a
French one would manufacture the very difference the artifact exists to measure, then hand it to the
slotter as though the lattice had caused it.

## 3 — Slot it

```python
prompt = fihrist.slot_prompt([record])
results = fihrist.validate(your_model(prompt), [record])

results[0].attached[0]
```

```json
{"axis": "Discipline", "parent": "Japanese history", "leaf": "Modern Japan (Meiji–Shōwa)",
 "confidence": "stretch", "culture": "Japanese", "status": "valid"}
```

Batch as many books into one `slot_prompt` call as your context window allows. The lattice index is
rendered first and identically every time, so a prompt-caching backend caches it across a whole run.

### Validation is not a formality

```python
fihrist.summarise(results)
# {'books': 1, 'attachments': 7, 'rejectedAttachments': 1,
#  'booksWithNoAttachment': 0, 'leavesPerBook': 7.0, 'byAxis': {...}}
```

Three things worth knowing:

- **Invented leaves are dropped.** Models produce plausible leaves in the artifact's own house style.
  An invented one that survives into your results is worse than a missing one, because it looks like
  evidence. Anything not on the lattice goes to `rejected` with a reason, never into `attached`.
- **A wrong parent is repaired, and the repair is recorded** in `parentRepaired`. Polyhierarchy means
  a leaf can hang from several parents; any true one is accepted.
- **Check `booksWithNoAttachment` first.** A batch that comes back structurally valid and
  substantively empty is the quietest way a run fails — the JSON parses, nothing raises, and the
  numbers are simply thin. That counter is where it shows.

## From the shell

```bash
fihrist verify                                    # hash check
fihrist axes                                      # the 10 axes
fihrist leaves --axis Genre --grep thriller
fihrist prompt enrich --title "I Am a Cat" --author "Natsume Sōseki"
fihrist prompt slot books.json > prompt.txt
fihrist validate response.json --books books.json --out slotted.json
```

---

## The claim, and its limits

Seven engines — Claude, GPT, DeepSeek, and local Qwen models from 30B to 235B — slotted the same
1,232 books onto this lattice. **All of them top-ranked India, China and Japan on the Place axis.** A
biased engine buries those. None did. The correction lives in the artifact rather than in a prompt or
a model, so swapping the model does not bring the bias back.

What is **not** established: a later 425-book corpus run, pre-registered and sealed, did not confirm
this at corpus scale — its three gates all counted Discipline leaves, and the registered engine
produces 0.20 Discipline leaves per book on fiction, so the probes largely measured the instrument
rather than the artifact. That run, its failure, and the reasoning are published alongside the
evidence rather than quietly dropped. See the corpus dataset.

Seven of the ten axes carry a published parity audit. Three — `Agent/Entity`, `Action/Process`,
`Thing/Matter` — are provisional and marked as such (`fihrist.PROVISIONAL_AXES`).

## The guard rules

`fihrist.GUARDS` is the anti-bias correction, carried verbatim from the run that produced the
published results. **Rewording it invalidates every parity claim attached to this artifact.** If you
need different behaviour, write your own prompt rather than editing those lines, and do not report
the result as a Fihrist placement.

## Cost

Fihrist itself is free and offline. Steps 2 and 3 are your own model calls — roughly one
search-enabled call and one slot call per book.

## Just want to look?

The evidence is being published as datasets — 425 books slotted independently by three engines, the
artifact with its audits, and a browsable view of the result. Until those land, everything the
package needs is in the wheel, and the source repo is
[bk2022-ui/lattice](https://github.com/bk2022-ui/lattice).

## Licence

Code: **Apache-2.0**. The artifact (`world-lattice.json`) and the corpus: **CC-BY-4.0**.

Attribution is the point. If the lattice is doing work for you, cite it.
