LATEXMK := latexmk SRCDIR := paper/src BUILDDIR := build TEX := main.tex JOBNAME := main PDF := paper/$(BUILDDIR)/$(JOBNAME).pdf VENV := .venv PYTHON := $(VENV)/bin/python PIP := $(VENV)/bin/pip PYTEST := $(VENV)/bin/pytest NX := npx nx SWEEP_ENV_FILE ?= .env.sweep WANDB_ENTITY ?= WANDB_PROJECT ?= phantom-pricing SWEEP_ID ?= LOCAL_TRAIN_ARGS ?= --algo ppo --total-timesteps 50000 AGENT_COUNT ?= 0 REPO_URL ?= BRANCH ?= main WORKDIR ?= $(HOME)/PHANTOM-agent AGENT_LOOP ?= 1 RETRY_SECONDS ?= 20 TRAIN_IMAGE_REF := us-central1-docker.pkg.dev/phantom-trc/phantom/phantom-trainer TPU_NAME ?= TPU_ZONE ?= us-central2-b TPU_PROJECT ?= phantom-trc TPU_REPO_DIR ?= /tmp/PHANTOM SWEEP_ENV_LOAD = set -a; [ -f "$(SWEEP_ENV_FILE)" ] && . "$(SWEEP_ENV_FILE)" || true; set +a .DEFAULT_GOAL := help .PHONY: help help: @echo "pdf.build pdf.watch pdf.clean | test.backend test.e2e test.all | web.dev | install | train | train.agent | train.bootstrap | train.tpu.pod | train.tpu.vm | train.tpu.vm.sweep | stats.lines" @echo "backend.server backend.provider backend.worker | platform.up platform.down platform.logs | docker.train.publish" @echo "" @echo "Local wandb run:" @echo " make train LOCAL_TRAIN_ARGS='--algo ppo --total-timesteps 50000'" @echo "" @echo "Local sweep agent from this repo:" @echo " make train.agent SWEEP_ID=entity/project/id AGENT_COUNT=5" @echo "" @echo "Bootstrap private repo worker from anywhere:" @echo " make train.bootstrap REPO_URL=https://github.com/org/repo.git BRANCH=main SWEEP_ID=entity/project/id" @echo "" @echo "Config source: $(SWEEP_ENV_FILE) (auto-loaded)" $(BUILDDIR): mkdir -p paper/$(BUILDDIR) .PHONY: pdf.build pdf.build: $(BUILDDIR) @bash paper/concat_code.sh @cd $(SRCDIR) && \ $(LATEXMK) -pdf -jobname=$(JOBNAME) -f \ -interaction=nonstopmode -file-line-error \ -r ../.latexmkrc \ -outdir=../$(BUILDDIR) $(TEX) .PHONY: pdf.watch pdf.watch: $(BUILDDIR) @cd $(SRCDIR) && \ $(LATEXMK) -pvc -pdf -jobname=$(JOBNAME) -f \ -interaction=nonstopmode -file-line-error \ -r ../.latexmkrc \ -outdir=../$(BUILDDIR) $(TEX) .PHONY: pdf.clean pdf.clean: @cd $(SRCDIR) && \ $(LATEXMK) -C -jobname=$(JOBNAME) -outdir=../$(BUILDDIR) || true rm -rf paper/$(BUILDDIR)/* .PHONY: test.backend test.backend: $(VENV) $(PYTEST) -v .PHONY: test.e2e test.e2e: @cd tests/e2e && npm install @cd tests/e2e && npx playwright install chromium @test -f tests/e2e/.env || cp tests/e2e/.env.example tests/e2e/.env @timeout 30 bash -c 'until curl -sf http://localhost:5000/health > /dev/null 2>&1; do sleep 1; done' || (echo "Backend not ready" && exit 1) @timeout 30 bash -c 'until curl -sf http://localhost:3000 > /dev/null 2>&1; do sleep 1; done' || (echo "Web app not ready" && exit 1) @timeout 30 bash -c 'until curl -sf http://localhost:8085/health > /dev/null 2>&1; do sleep 1; done' || (echo "Airflow not ready" && exit 1) @cd tests/e2e && npm test .PHONY: test.all test.all: test.backend test.e2e .PHONY: web.dev web.dev: @cd web && npm install && npm run dev $(VENV): python3 -m venv $(VENV) $(PIP) install --upgrade pip .PHONY: install install: $(VENV) $(PIP) install -r requirements.txt .PHONY: train train: install @$(SWEEP_ENV_LOAD); test -n "$$WANDB_API_KEY" || (echo "WANDB_API_KEY required — set it in $(SWEEP_ENV_FILE)" && exit 1) @$(SWEEP_ENV_LOAD); WANDB_API_KEY="$$WANDB_API_KEY" WANDB_ENTITY="$(WANDB_ENTITY)" WANDB_PROJECT="$(WANDB_PROJECT)" \ $(PYTHON) -m engine.train $(LOCAL_TRAIN_ARGS) .PHONY: train.agent train.agent: install @$(SWEEP_ENV_LOAD); test -n "$$WANDB_API_KEY" || (echo "WANDB_API_KEY required — set it in $(SWEEP_ENV_FILE)" && exit 1) @test -n "$(SWEEP_ID)" || (echo "SWEEP_ID required, e.g. SWEEP_ID=entity/project/id" && exit 1) @$(SWEEP_ENV_LOAD); WANDB_API_KEY="$$WANDB_API_KEY" WANDB_ENTITY="$(WANDB_ENTITY)" WANDB_PROJECT="$(WANDB_PROJECT)" \ $(PYTHON) -m engine.train --sweep-agent --sweep-id "$(SWEEP_ID)" \ $(if $(filter-out 0,$(AGENT_COUNT)),--count $(AGENT_COUNT),) .PHONY: train.bootstrap train.bootstrap: @$(SWEEP_ENV_LOAD); test -n "$$WANDB_API_KEY" || (echo "WANDB_API_KEY required — set it in $(SWEEP_ENV_FILE)" && exit 1) @$(SWEEP_ENV_LOAD); test -n "$$GITHUB_TOKEN" || (echo "GITHUB_TOKEN required — set it in $(SWEEP_ENV_FILE)" && exit 1) @test -n "$(REPO_URL)" || (echo "REPO_URL required, e.g. REPO_URL=https://github.com/org/repo.git" && exit 1) @test -n "$(SWEEP_ID)" || (echo "SWEEP_ID required, e.g. SWEEP_ID=entity/project/id" && exit 1) @$(SWEEP_ENV_LOAD); \ WANDB_API_KEY="$$WANDB_API_KEY" \ WANDB_ENTITY="$(WANDB_ENTITY)" \ WANDB_PROJECT="$(WANDB_PROJECT)" \ GITHUB_TOKEN="$$GITHUB_TOKEN" \ REPO_URL="$(REPO_URL)" \ BRANCH="$(BRANCH)" \ WORKDIR="$(WORKDIR)" \ SWEEP_ID="$(SWEEP_ID)" \ AGENT_COUNT="$(AGENT_COUNT)" \ AGENT_LOOP="$(AGENT_LOOP)" \ RETRY_SECONDS="$(RETRY_SECONDS)" \ bash scripts/wandb_agent_bootstrap.sh .PHONY: stats.lines stats.lines: @find . \( -path '*/node_modules' -o -path '*/.venv' -o -path '*/venv' \) -prune -o \ \( -name "*.ts" -o -name "*.py" \) -type f -print0 | xargs -0 cat | wc -l .PHONY: wordcount wordcount: @echo "Counting words in main text (excluding appendix)..." @texcount -nosub -total -sum -1 \ $(SRCDIR)/chapters/01-intro.tex \ $(SRCDIR)/chapters/02-literature-review.tex \ $(SRCDIR)/chapters/03-methodology.tex \ $(SRCDIR)/chapters/04-results.tex \ $(SRCDIR)/chapters/05-discussion.tex \ $(SRCDIR)/chapters/06-conclusion.tex .PHONY: docker.train.publish docker.train.publish: docker build -f docker/Trainer.dockerfile --target gpu -t $(TRAIN_IMAGE_REF):gpu-latest . docker push $(TRAIN_IMAGE_REF):gpu-latest docker build -f docker/Trainer.dockerfile --target tpu -t $(TRAIN_IMAGE_REF):tpu-latest . docker push $(TRAIN_IMAGE_REF):tpu-latest .PHONY: train.tpu.pod train.tpu.pod: @test -n "$(TPU_NAME)" || (echo "TPU_NAME required, e.g. TPU_NAME=TPUlong" && exit 1) @test -n "$(SWEEP_ID)" || (echo "SWEEP_ID required, e.g. SWEEP_ID=entity/project/id" && exit 1) @$(SWEEP_ENV_LOAD); test -n "$$WANDB_API_KEY" || (echo "WANDB_API_KEY required — set it in $(SWEEP_ENV_FILE)" && exit 1) gcloud compute tpus tpu-vm scp scripts/tpu_pod_run.sh $(TPU_NAME):/tmp/tpu_pod_run.sh \ --zone=$(TPU_ZONE) --project=$(TPU_PROJECT) --worker=all @$(SWEEP_ENV_LOAD); \ gcloud compute tpus tpu-vm ssh $(TPU_NAME) \ --zone=$(TPU_ZONE) --project=$(TPU_PROJECT) --worker=all \ --command="WANDB_API_KEY='$$WANDB_API_KEY' SWEEP_ID='$(SWEEP_ID)' AGENT_COUNT='$(AGENT_COUNT)' sh /tmp/tpu_pod_run.sh" .PHONY: train.tpu.vm.prepare train.tpu.vm.prepare: @test -n "$(TPU_NAME)" || (echo "TPU_NAME required, e.g. TPU_NAME=TPUlong" && exit 1) TPU_NAME="$(TPU_NAME)" TPU_ZONE="$(TPU_ZONE)" TPU_PROJECT="$(TPU_PROJECT)" \ LOCAL_REPO_DIR="$(CURDIR)" REMOTE_REPO_DIR="$(TPU_REPO_DIR)" \ sh scripts/tpu_sync_repo.sh gcloud compute tpus tpu-vm scp scripts/tpu_vm_train.sh $(TPU_NAME):/tmp/tpu_vm_train.sh \ --zone=$(TPU_ZONE) --project=$(TPU_PROJECT) --worker=all .PHONY: train.tpu.vm.run train.tpu.vm.run: @test -n "$(TPU_NAME)" || (echo "TPU_NAME required, e.g. TPU_NAME=TPUlong" && exit 1) @test -n "$(LOCAL_TRAIN_ARGS)" || (echo "LOCAL_TRAIN_ARGS required, e.g. --algo ppo --jax --total-timesteps 200000" && exit 1) @$(SWEEP_ENV_LOAD); \ gcloud compute tpus tpu-vm ssh $(TPU_NAME) \ --zone=$(TPU_ZONE) --project=$(TPU_PROJECT) --worker=all \ --command="REPO_DIR='$(TPU_REPO_DIR)' TRAIN_ARGS='$(LOCAL_TRAIN_ARGS)' WANDB_API_KEY='$$WANDB_API_KEY' sh /tmp/tpu_vm_train.sh" .PHONY: train.tpu.vm train.tpu.vm: train.tpu.vm.prepare train.tpu.vm.run .PHONY: train.tpu.vm.sweep train.tpu.vm.sweep: @test -n "$(TPU_NAME)" || (echo "TPU_NAME required, e.g. TPU_NAME=TPUlong" && exit 1) @test -n "$(SWEEP_ID)" || (echo "SWEEP_ID required, e.g. SWEEP_ID=lusiana/phantom-pricing/abc123" && exit 1) @$(SWEEP_ENV_LOAD); test -n "$$WANDB_API_KEY" || (echo "WANDB_API_KEY required — set it in $(SWEEP_ENV_FILE)" && exit 1) @$(SWEEP_ENV_LOAD); WANDB_API_KEY="$$WANDB_API_KEY" \ python3 scripts/tpu_vm_sweep_agent.py \ --sweep-id "$(SWEEP_ID)" \ --tpu-name "$(TPU_NAME)" \ --tpu-zone "$(TPU_ZONE)" \ --tpu-project "$(TPU_PROJECT)" \ --tpu-repo-dir "$(TPU_REPO_DIR)" \ $(if $(filter-out 0,$(AGENT_COUNT)),--count $(AGENT_COUNT),) .PHONY: backend.server backend.provider backend.worker platform.up platform.down platform.logs backend.server: @$(NX) run backend-server:dev backend.provider: @$(NX) run pricing-provider:dev backend.worker: @$(NX) run backend-worker:dev platform.up: @$(NX) run platform:up platform.down: @$(NX) run platform:down platform.logs: @$(NX) run platform:logs .PHONY: pdf clean watch run.webapp test count-lines all pdf: @$(NX) run paper:build clean: @$(NX) run paper:clean watch: @$(NX) run paper:watch run.webapp: @$(NX) run web:dev test: @$(NX) run research:test count-lines: @$(NX) run research:stats all: @$(NX) run paper:build