arborist/pyproject.toml
russell@unturf.com 727cb1bd96
feat: #000061 cold-pack distribution tier (boto3 S3-compat + DVD-R safe-fit)
Ship arborist corpus state to new peers and DVD-R archival via
point-in-time tar.zst packs. One artifact serves both channels —
bucket+CDN delivery and physical-media archival.

Bucket holds packs only. Pack key = hash_leaf(manifest_bytes), so same
chunk set on two writers produces the same pack_hash and upload is
idempotent. Each pack pins the corpus snapshot_root it covers in audit
+ result body — packs are delayed snapshots, not live mirrors;
falsifications between repacks produce new pack_hashes.

stream_packs runs streaming zstd over tarfile, peeking compressed-buffer
size after each chunk via FLUSH_BLOCK (preserves dictionary). Default
cap 4_400_000_000 — 4.4 GB DVD-R safe-fit, ~6.5% buffer below the
4.7 GB marketing capacity to absorb ISO9660 overhead, growisofs
lead-in/lead-out, media variance, and drive-edge refusal. Each disc
fills to ~4.4 GB recorded data, not the ~1.5 GB an uncompressed cap
produced.

One backend class (S3CompatibleBackend via boto3 + endpoint_url) covers
AWS S3, DO Spaces, R2, B2, GCS S3-interop, MinIO. Optional dep
[object-store] = boto3>=1.34; dev extras pull moto for the wire test.
Voyeur: credentials via AWS_ACCESS_KEY_ID/_SECRET_ACCESS_KEY env or
~/.aws/credentials, never printed; only endpoint URL + bucket name
surface in logs.

CLI: arborist cold {pack,unpack,stats}. Makefile: cold-pack,
cold-pack-dvd (local-dir output for growisofs), cold-unpack, cold-stats.

Sizing for current shards (14.1M chunks, ~17 GB compressed): ~4 packs
at the default cap, ~\$0.34/mo DO Spaces storage, ~\$0.0001/fresh-peer
hydrate.

Always-on raw-UTF-8 leaf store (per ticket "Hard invariants") deferred
— packs-only for now, backfill later.

2557 passed, 28 skipped, 1 xfailed.
2026-05-25 20:23:44 -04:00

164 lines
6 KiB
TOML

[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "arborist"
version = "0.0.1"
description = "An arborist for trees and forests of cross-linked information"
readme = "README.md"
license = { text = "AGPL-3.0-only" }
requires-python = ">=3.10"
authors = [
{ name = "Russell Ballestrini", email = "russell@unturf.com" },
{ name = "foxhop" },
{ name = "TimeHexOn" },
]
dependencies = [
"httpx>=0.27",
"zstandard>=0.22",
"cryptography>=42",
]
[project.optional-dependencies]
html = [
"selectolax>=0.3",
]
wikitext = [
"mwparserfromhell>=0.6",
]
mesh = [
# httpx is already in core deps; mesh wire only depends on stdlib +
# cryptography (also core). This extras block exists as the documented
# opt-in surface even though no extra packages are required today.
]
math = [
# Symbolic algebra/calculus π* substrate (ticket #000030). SymPy is
# ~30 MB installed; pulling it into core deps would inflate every
# fresh checkout. Tests skip via pytest.importorskip when absent.
"sympy>=1.13",
]
hessian = [
# Phi_alignment_probe (ticket #000034 Phase 1a). Lanczos top-k +
# bottom-k eigendecomposition for measuring whether v7's frozen
# linear projection W aligns with the loss Hessian's low-eigenvalue
# subspace. Numpy + scipy together ~80 MB; gated separately from
# core to keep the default install lightweight. Tests skip via
# pytest.importorskip when absent. Install with:
# pip install 'arborist[hessian]'
"numpy>=1.26",
"scipy>=1.11",
]
crawler = [
# Verbatim lift from agents.ai.unturf.com/core. Off by default — the
# default test suite never imports the crawler. Install with:
# pip install 'arborist[crawler]'
# then run `make test-crawler`.
"aiohttp>=3.8",
"beautifulsoup4>=4.11",
"lxml>=4.9",
"html5lib>=1.1",
"html2text>=2024.2.26",
"miniuri>=1.1",
"feedparser>=6.0",
"Pillow>=10.0",
"cairosvg>=2.7",
"pypdf>=4.0",
]
vec = [
# Optional sqlite-vec semantic retrieval backend (ticket #000039).
# sqlite-vec ships only the loadable SQLite extension (~1 MB);
# fastembed pulls onnxruntime + tokenizers + huggingface-hub
# (~150 MB) and downloads the bge-small-en-v1.5 ONNX model
# (~130 MB) on first use. Gated separately so a fresh checkout
# stays python3.12 + venv + sqlite3. CLI surfaces `arborist embed`
# / `--backend vec` only when `sqlite_vec` imports. Install with:
# pip install 'arborist[vec]'
# (sentence-transformers is the heavier "official" embedder path
# the ticket §5 names; fastembed is the lightweight ONNX one.)
"sqlite-vec>=0.1.9",
"fastembed>=0.4",
]
nli = [
# Sentence-pair NLI for the #000049 Phase-2 *shadow* path
# (arborist/qa/nli/) — measures whether a clause-level contradiction
# veto would demote a weakly-grounded answer; never touches
# audit_mode. transformers + a CPU torch is ~600 MB installed, so it
# is gated hard out of core / dev — a fresh checkout stays
# python3.12 + venv + sqlite3, and the default test suite skips the
# NLI tests via pytest.importorskip when this extra is absent.
# Install with:
# pip install 'arborist[nli]'
# `optimum[onnxruntime]` gives the ONNX-export + int8-quantize path
# (`bench/scripts/export_nli_onnx.py`, `make export-nli-onnx`):
# `onnxruntime` on a quantized cross-encoder is ~2-4x faster on CPU
# than the torch forward path; `ShadowNLI._ensure_loaded` auto-prefers
# an export if it finds one. torch is still here because `optimum`'s
# exporter uses it, and it's the fallback when no export exists; a
# Phase-3 runtime could ship an `[nli-onnx]`-only extra (onnxruntime,
# no torch) once the export is committed/distributed (cf. [vec]).
"transformers>=4.40",
"torch>=2.2",
"sentencepiece>=0.2",
"protobuf>=4.0",
"optimum[onnxruntime]>=1.20",
]
object-store = [
# Cold-object eviction tier (ticket #000061). Pushes chunk bodies to an
# S3-compatible bucket keyed by leaf_hash so the corpus can grow past
# one machine while the Merkle tree stays intact. One backend covers
# AWS S3, DigitalOcean Spaces, Cloudflare R2, Backblaze B2, GCS
# (S3 interop), and MinIO — boto3 with a per-provider endpoint_url.
# Gated separately so a fresh checkout stays python3.12 + venv +
# sqlite3; tests skip via pytest.importorskip when absent. Install with:
# pip install 'arborist[object-store]'
# Credentials use boto3's standard discovery (env vars,
# ~/.aws/credentials, IAM role); never read into arborist code. The
# wire-level test gated additionally on `moto`.
"boto3>=1.34",
]
mt = [
# Local machine-translation engine for the #000056 "Operation
# Sandwich" cross-language grounding edges (arborist/qa/mt/):
# translate the query es->en (retrieval-side) and render the
# *verified English* answer en->es for display only. Helsinki-NLP
# opus-mt (MarianMT) is Apache-2.0, hash-pinnable by HF revision
# (reproducible — provenance-critical for a Merkle-replay system),
# and es<->en is its best-resourced pair. Hard out of core/dev like
# [nli]: a fresh checkout stays python3.12 + venv + sqlite3, and the
# default suite drives the sandwich via a deterministic
# StubTranslator so it never needs these weights. Install with:
# pip install 'arborist[mt]'
# Weights are NOT in the repo; they cache under
# ~/.arborist/models/mt/. Never Hermes-3-8B (8B unfit for
# human-language translation); never an external API (zero egress /
# Operation Voyeur; opaque API "versions" are provenance-hostile).
"transformers>=4.40",
"torch>=2.2",
"sentencepiece>=0.2",
"protobuf>=4.0",
"sacremoses>=0.1",
]
dev = [
"pytest>=8",
"pytest-asyncio>=0.23",
"pytest-xdist>=3.5",
"arborist[html]",
"arborist[wikitext]",
"arborist[mesh]",
"arborist[crawler]",
"arborist[math]",
"arborist[hessian]",
"arborist[vec]",
"arborist[object-store]",
# moto is the wire-level boto3 test stub; only needed to run
# tests/test_cold_object_boto3.py (the default suite uses MemoryBackend).
"moto>=5.0",
]
[project.scripts]
arborist = "arborist.cli:main"
[tool.setuptools.packages.find]
where = ["."]
include = ["arborist*"]