.PHONY: help setup test validate preflight data sft dpo rl graphs all clean .DEFAULT_GOAL := help PY ?= .venv/bin/python HUPD ?= hupd/sample/2016 AUG ?= hupd/aug2014 TOKENIZER ?= tokenizer_qwen35.json help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ | awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-14s\033[0m %s\n", $$1, $$2}' setup: ## Create .venv (Python 3.12) and install dependencies uv venv --python 3.12 .venv uv pip install -r requirements.txt @test -f .env || (cp .env.example .env && echo "Created .env -- add your TINKER_API_KEY") test: ## Run the offline test suite (no API key, no data rebuild needed) $(PY) tests/test_rewards.py $(PY) tests/test_dataset.py validate: ## Structural checks on the built dataset $(PY) scripts/validate_dataset.py data/sft preflight: ## Verify everything end to end, including the Tinker API $(PY) scripts/preflight.py calibrate: ## Re-check the reward function against real granted claims $(PY) claim_drafter/rewards.py data/sft/train.jsonl 2000 # ---------------------------------------------------------------- data # The built datasets are ~600MB and are NOT committed. manifests/ holds the # provenance (patent numbers) so these rebuild byte-identically. hupd: ## Download the HUPD Jan-2016 sample (~370MB) mkdir -p hupd curl -L -o hupd/sample-jan-2016.tar.gz \ https://huggingface.co/datasets/HUPD/hupd/resolve/main/data/sample-jan-2016.tar.gz tar -xzf hupd/sample-jan-2016.tar.gz -C hupd tokenizer: ## Download the Qwen3.5 tokenizer (exact token counts) curl -L -o $(TOKENIZER) \ https://huggingface.co/Qwen/Qwen3.5-9B/resolve/main/tokenizer.json data: hupd tokenizer ## Rebuild the SFT dataset end to end (~1 hour, mostly fetching) $(PY) scripts/select_targets.py --hupd $(HUPD) --output data/cache/targets_granted.txt $(PY) scripts/fetch_claims.py --kind granted \ --input data/cache/targets_granted.txt --output data/cache/granted_claims.jsonl $(PY) scripts/augment_thin_domains.py 2014 $(AUG) $(PY) scripts/select_targets.py --hupd $(AUG) --output data/cache/targets_aug.txt \ --domains biotech_life_sciences,energy_environment,pharma_medical_devices $(PY) scripts/fetch_claims.py --kind granted \ --input data/cache/targets_aug.txt --output data/cache/granted_claims.jsonl $(PY) scripts/build_sft_dataset.py $(TOKENIZER) --sources $(HUPD) $(AUG) --out-dir data/sft $(PY) scripts/validate_dataset.py data/sft dpo-data: ## Build the DPO preference pairs (needs data/sft first) $(PY) scripts/select_targets.py --hupd $(HUPD) --output data/cache/targets_pubs.tsv \ --publications --manifest data/sft/manifest.jsonl $(PY) scripts/fetch_claims.py --kind filed \ --input data/cache/targets_pubs.tsv --output data/cache/filed_claims.jsonl $(PY) scripts/build_dpo_pairs.py $(TOKENIZER) --out data/dpo # ---------------------------------------------------------------- training sft: ## Stage 1: supervised fine-tuning $(PY) training/train_sft.py --epochs 2 dpo: ## Stage 2: DPO. CKPT defaults to the SFT run's final checkpoint $(PY) training/train_dpo.py --from-checkpoint \ $(or $(CKPT),$(shell $(PY) scripts/last_checkpoint.py runs/sft)) rl: ## Stage 3: GRPO. CKPT defaults to the DPO run's final checkpoint $(PY) training/train_rl.py --from-checkpoint \ $(or $(CKPT),$(shell $(PY) scripts/last_checkpoint.py runs/dpo)) # ---------------------------------------------------------------- reporting graphs: ## Build charts + summary from every stage's logs (run after `all`) $(PY) scripts/plot_runs.py --runs runs --out runs/graphs all: ## Run the whole pipeline end to end, then draw the graphs $(MAKE) sft $(MAKE) dpo $(MAKE) rl $(MAKE) graphs clean: ## Remove generated data and run artifacts (keeps manifests/ and samples/) rm -rf data/sft data/dpo data/cache runs export __pycache__ \ claim_drafter/__pycache__ scripts/__pycache__ tests/__pycache__