diff --git a/.github/workflows/solana-build-anchor-programs.yml b/.github/workflows/solana-build-anchor-programs.yml index 48422cae0..164ad89b8 100644 --- a/.github/workflows/solana-build-anchor-programs.yml +++ b/.github/workflows/solana-build-anchor-programs.yml @@ -16,6 +16,8 @@ jobs: uses: actions/cache@v4 with: path: validator-image.tar.gz + # FIXME: this key will make the cache succeed even if some other nix files which affect it change. + # we should *really* build this with nix instead of a manual hashFiles / cache here key: validator-image-${{ runner.os }}-${{ hashFiles('shared/coordinator/src/coordinator.rs', 'architectures/decentralized/solana-coordinator/**/*.rs', 'architectures/decentralized/solana-coordinator/**/*.toml', 'architectures/decentralized/solana-coordinator/Cargo.lock', 'architectures/decentralized/solana-authorizer/**/*.rs', 'architectures/decentralized/solana-authorizer/**/*.toml', 'architectures/decentralized/solana-authorizer/Cargo.lock', 'docker/test/psyche_solana_validator_entrypoint.sh', 'nix/docker.nix', 'flake.lock') }} lookup-only: true diff --git a/.github/workflows/solana-integration-test-base.yml b/.github/workflows/solana-integration-test-base.yml index 2849cdb89..4307a2c13 100644 --- a/.github/workflows/solana-integration-test-base.yml +++ b/.github/workflows/solana-integration-test-base.yml @@ -39,10 +39,6 @@ jobs: substituters = https://cache.nixos.org/ https://cache.garnix.io/ https://nix-community.cachix.org trusted-public-keys = cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= - - name: Install just - run: | - nix profile install nixpkgs#just - # Step 1: Get Validator Image from cache - name: Get Validator Image from cache id: cache-validator @@ -79,17 +75,9 @@ jobs: echo "Disk space before client build" df -h - # Calculate the derivation hash - echo "Calculating derivation path" - DRV_PATH=$(nix eval --raw .#docker-psyche-solana-test-client-no-python.drvPath) - echo "Derivation path: $DRV_PATH" - - OUT_PATH=$(nix derivation show $DRV_PATH | jq -r '.[].outputs.out.path') - echo "Output path: $OUT_PATH" - - # download from Garnix cache first + # download from Garnix cache echo "Attempting to fetch from Garnix cache" - nix-store --realise $OUT_PATH --option substitute true + OUT_PATH=$(nix build .#docker-psyche-solana-test-client-no-python --no-link --print-out-paths) # Load the image into Docker $OUT_PATH | docker load @@ -97,12 +85,14 @@ jobs: echo "Disk space after client build" df -h - # Clean Nix store after client build - - name: Clean after client build + - name: Build run-manager binary run: | - # Clean up the path file - rm -f client-image-path.txt + nix build --out-link run-manager .#run-manager + nix build --out-link _keep_tests_binary .#test-psyche-decentralized-testing-integration_tests + # Clean Nix store + - name: Clean after client build + run: | # Clean nix store garbage nix-collect-garbage -d nix store optimise @@ -129,4 +119,4 @@ jobs: env: HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | - nix develop --command cargo test --release -p psyche-decentralized-testing --test integration_tests -- --nocapture "${{ inputs.test-name }}" + cd architectures/decentralized/testing/ && nix run .#test-psyche-decentralized-testing-integration_tests -- --nocapture "${{ inputs.test-name }}" diff --git a/Cargo.lock b/Cargo.lock index 24ee94226..85f3b06da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4841,9 +4841,9 @@ checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -7123,6 +7123,7 @@ version = "0.2.0" dependencies = [ "anyhow", "clap", + "clap-markdown", "iroh", "iroh-blobs", "iroh-gossip", @@ -7480,7 +7481,7 @@ dependencies = [ [[package]] name = "pyo3-tch" version = "0.22.0" -source = "git+https://github.com/jquesnelle/tch-rs.git?rev=11d1ca2ef6dbd3f1e5b0986fab0a90fbb6734496#11d1ca2ef6dbd3f1e5b0986fab0a90fbb6734496" +source = "git+https://github.com/jquesnelle/tch-rs.git?rev=dda507e05a776547a112b6854d1e611684f8c729#dda507e05a776547a112b6854d1e611684f8c729" dependencies = [ "pyo3", "tch", @@ -11810,7 +11811,7 @@ dependencies = [ [[package]] name = "tch" version = "0.22.0" -source = "git+https://github.com/jquesnelle/tch-rs.git?rev=11d1ca2ef6dbd3f1e5b0986fab0a90fbb6734496#11d1ca2ef6dbd3f1e5b0986fab0a90fbb6734496" +source = "git+https://github.com/jquesnelle/tch-rs.git?rev=dda507e05a776547a112b6854d1e611684f8c729#dda507e05a776547a112b6854d1e611684f8c729" dependencies = [ "half", "lazy_static", @@ -12308,7 +12309,7 @@ dependencies = [ [[package]] name = "torch-sys" version = "0.22.0" -source = "git+https://github.com/jquesnelle/tch-rs.git?rev=11d1ca2ef6dbd3f1e5b0986fab0a90fbb6734496#11d1ca2ef6dbd3f1e5b0986fab0a90fbb6734496" +source = "git+https://github.com/jquesnelle/tch-rs.git?rev=dda507e05a776547a112b6854d1e611684f8c729#dda507e05a776547a112b6854d1e611684f8c729" dependencies = [ "anyhow", "cc", @@ -12870,9 +12871,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -12881,27 +12882,14 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -12910,9 +12898,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12920,22 +12908,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn 2.0.106", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] @@ -12955,9 +12943,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 7b3968ade..36f1ba565 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,9 +79,9 @@ indicatif = "0.17.5" tokenizers = { version = "0.20.0", default-features = false, features = [ "onig", ] } -tch = { git = "https://github.com/jquesnelle/tch-rs.git", rev = "11d1ca2ef6dbd3f1e5b0986fab0a90fbb6734496" } -torch-sys = { git = "https://github.com/jquesnelle/tch-rs.git", rev = "11d1ca2ef6dbd3f1e5b0986fab0a90fbb6734496" } -pyo3-tch = { git = "https://github.com/jquesnelle/tch-rs.git", rev = "11d1ca2ef6dbd3f1e5b0986fab0a90fbb6734496" } +tch = { git = "https://github.com/jquesnelle/tch-rs.git", rev = "dda507e05a776547a112b6854d1e611684f8c729" } +torch-sys = { git = "https://github.com/jquesnelle/tch-rs.git", rev = "dda507e05a776547a112b6854d1e611684f8c729" } +pyo3-tch = { git = "https://github.com/jquesnelle/tch-rs.git", rev = "dda507e05a776547a112b6854d1e611684f8c729" } #tch = { path = "../tch-rs" } #torch-sys = { path = "../tch-rs/torch-sys" } #pyo3-tch = { path = "../tch-rs/pyo3-tch" } diff --git a/architectures/decentralized/testing/packages.nix b/architectures/decentralized/testing/packages.nix new file mode 100644 index 000000000..ccc2531a6 --- /dev/null +++ b/architectures/decentralized/testing/packages.nix @@ -0,0 +1,18 @@ +{ + psycheLib, + pkgs, + inputs, + ... +}: + +let + system = pkgs.stdenv.hostPlatform.system; +in +psycheLib.buildRustPackage { + cratePath = ./.; + # all tests need solana CLI and just + buildInputs.test = [ + inputs.solana-pkgs.packages.${system}.solana + pkgs.just + ]; +} diff --git a/architectures/inference-only/inference-node/Cargo.toml b/architectures/inference-only/inference-node/Cargo.toml index 0b07b76fe..b1a18d74f 100644 --- a/architectures/inference-only/inference-node/Cargo.toml +++ b/architectures/inference-only/inference-node/Cargo.toml @@ -25,7 +25,8 @@ anyhow.workspace = true tracing.workspace = true tracing-subscriber = { version = "0.3", features = ["env-filter"] } -clap = { version = "4", features = ["derive"] } +clap.workspace = true +clap-markdown.workspace = true iroh.workspace = true iroh-blobs.workspace = true diff --git a/architectures/inference-only/inference-node/packages.nix b/architectures/inference-only/inference-node/packages.nix new file mode 100644 index 000000000..0bd98dbb9 --- /dev/null +++ b/architectures/inference-only/inference-node/packages.nix @@ -0,0 +1,12 @@ +{ psycheLib, ... }: + +psycheLib.buildRustPackage { + needsPython = true; + needsGpu = true; + cratePath = ./.; + # vllm doesn't build on macos + supportedSystems = [ + "x86_64-linux" + "aarch64-linux" + ]; +} diff --git a/architectures/inference-only/inference-node/src/main.rs b/architectures/inference-only/inference-node/src/main.rs index bdc185cf4..4776458ae 100644 --- a/architectures/inference-only/inference-node/src/main.rs +++ b/architectures/inference-only/inference-node/src/main.rs @@ -9,7 +9,7 @@ //! - Supports dynamic checkpoint reloading use anyhow::{Context, Result}; -use clap::Parser; +use clap::{Args as ClapArgs, Parser, Subcommand}; use psyche_inference::{InferenceGossipMessage, InferenceNode}; use psyche_metrics::ClientMetrics; use psyche_network::{DiscoveryMode, NetworkConnection, NetworkEvent, RelayKind, allowlist}; @@ -20,9 +20,31 @@ use tracing::{debug, error, info, warn}; #[derive(Parser, Debug)] #[command(name = "psyche-inference-node")] -struct Args { +struct Cli { + #[command(subcommand)] + command: Option, + + #[command(flatten)] + run_args: RunArgs, +} + +#[derive(Subcommand, Debug)] +enum Commands { + /// Run the inference node (default) + Run(RunArgs), + + // Prints the help, optionally as markdown. Used for docs generation. + #[clap(hide = true)] + PrintAllHelp { + #[arg(long, required = true)] + markdown: bool, + }, +} + +#[derive(ClapArgs, Debug, Clone)] +struct RunArgs { #[arg(long)] - model_name: String, + model_name: Option, #[arg(long, default_value = "1")] tensor_parallel_size: usize, @@ -56,27 +78,44 @@ async fn main() -> Result<()> { ) .init(); - let args = Args::parse(); + let cli = Cli::parse(); + + // If no subcommand is provided, default to run with the flattened args + let run_args = match cli.command { + Some(Commands::PrintAllHelp { markdown }) => { + assert!(markdown); + clap_markdown::print_help_markdown::(); + return Ok(()); + } + Some(Commands::Run(args)) => args, + None => cli.run_args, + }; + + let model_name = run_args.model_name.context("--model-name is required")?; info!("Starting Psyche Inference Node"); - info!("Model: {}", args.model_name); - info!("Tensor Parallel Size: {}", args.tensor_parallel_size); - info!("GPU Memory Utilization: {}", args.gpu_memory_utilization); + info!("Model: {}", model_name); + info!("Tensor Parallel Size: {}", run_args.tensor_parallel_size); + info!( + "GPU Memory Utilization: {}", + run_args.gpu_memory_utilization + ); - let discovery_mode: DiscoveryMode = args + let discovery_mode: DiscoveryMode = run_args .discovery_mode .parse() .map_err(|e| anyhow::anyhow!("Invalid discovery mode: {}", e))?; - let relay_kind: RelayKind = args + let relay_kind: RelayKind = run_args .relay_kind .parse() .map_err(|e| anyhow::anyhow!("Invalid relay kind: {}", e))?; - let capabilities: Vec = if args.capabilities.is_empty() { + let capabilities: Vec = if run_args.capabilities.is_empty() { vec![] } else { - args.capabilities + run_args + .capabilities .split(',') .map(|s| s.trim().to_string()) .collect() @@ -90,15 +129,15 @@ async fn main() -> Result<()> { info!("Initializing vLLM engine..."); let mut inference_node = InferenceNode::new( - args.model_name.clone(), - Some(args.tensor_parallel_size), - Some(args.gpu_memory_utilization), + model_name.clone(), + Some(run_args.tensor_parallel_size), + Some(run_args.gpu_memory_utilization), ); inference_node .initialize( - Some(args.tensor_parallel_size), - Some(args.gpu_memory_utilization), + Some(run_args.tensor_parallel_size), + Some(run_args.gpu_memory_utilization), ) .context("Failed to initialize vLLM engine")?; @@ -131,7 +170,7 @@ async fn main() -> Result<()> { // Announce availability via gossip let availability_msg = InferenceGossipMessage::NodeAvailable { - model_name: args.model_name.clone(), + model_name: model_name.clone(), checkpoint_id: None, // TODO: Track actual checkpoint when reloading - do we need this? capabilities: capabilities.clone(), }; diff --git a/flake.lock b/flake.lock index 67a262751..e690f0f15 100644 --- a/flake.lock +++ b/flake.lock @@ -10,11 +10,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1761656077, - "narHash": "sha256-lsNWuj4Z+pE7s0bd2OKicOFq9bK86JE0ZGeKJbNqb94=", + "lastModified": 1762618334, + "narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=", "owner": "ryantm", "repo": "agenix", - "rev": "9ba0d85de3eaa7afeab493fed622008b6e4924f5", + "rev": "fcdea223397448d35d9b31f798479227e80183f6", "type": "github" }, "original": { @@ -54,11 +54,11 @@ }, "crane": { "locked": { - "lastModified": 1762189950, - "narHash": "sha256-aotggLUXjlDGqKWibGPQcMZJGgdr79S21ISrv1Wz6RI=", + "lastModified": 1768873933, + "narHash": "sha256-CfyzdaeLNGkyAHp3kT5vjvXhA1pVVK7nyDziYxCPsNk=", "owner": "ipetkov", "repo": "crane", - "rev": "50700219af884287ad7c85507e2f163b23a027a9", + "rev": "0bda7e7d005ccb5522a76d11ccfbf562b71953ca", "type": "github" }, "original": { @@ -90,11 +90,11 @@ ] }, "locked": { - "lastModified": 1762040540, - "narHash": "sha256-z5PlZ47j50VNF3R+IMS9LmzI5fYRGY/Z5O5tol1c9I4=", + "lastModified": 1768135262, + "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "0010412d62a25d959151790968765a70c436598b", + "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", "type": "github" }, "original": { @@ -213,11 +213,11 @@ ] }, "locked": { - "lastModified": 1760663237, - "narHash": "sha256-BflA6U4AM1bzuRMR8QqzPXqh8sWVCNDzOdsxXEguJIc=", + "lastModified": 1769069492, + "narHash": "sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W+xc49RL/U=", "owner": "cachix", "repo": "git-hooks.nix", - "rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37", + "rev": "a1ef738813b15cf8ec759bdff5761b027e3e1d23", "type": "github" }, "original": { @@ -300,11 +300,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1762111121, - "narHash": "sha256-4vhDuZ7OZaZmKKrnDpxLZZpGIJvAeMtK6FKLJYUtAdw=", + "lastModified": 1769018530, + "narHash": "sha256-MJ27Cy2NtBEV5tsK+YraYr2g851f3Fl1LpNHDzDX15c=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b3d51a0365f6695e7dd5cdf3e180604530ed33b4", + "rev": "88d3861acdd3d2f0e361767018218e51810df8a1", "type": "github" }, "original": { @@ -339,11 +339,11 @@ "uv2nix": "uv2nix" }, "locked": { - "lastModified": 1761781027, - "narHash": "sha256-YDvxPAm2WnxrznRqWwHLjryBGG5Ey1ATEJXrON+TWt8=", + "lastModified": 1763662255, + "narHash": "sha256-4bocaOyLa3AfiS8KrWjZQYu+IAta05u3gYZzZ6zXbT0=", "owner": "pyproject-nix", "repo": "build-system-pkgs", - "rev": "795a980d25301e5133eca37adae37283ec3c8e66", + "rev": "042904167604c681a090c07eb6967b4dd4dae88c", "type": "github" }, "original": { @@ -380,11 +380,11 @@ ] }, "locked": { - "lastModified": 1760402624, - "narHash": "sha256-jF6UKLs2uGc2rtved8Vrt58oTWjTQoAssuYs/0578Z4=", + "lastModified": 1768984832, + "narHash": "sha256-Q/5ICDALWlG+MbfLoghvXb3K9S0jqGt0mj8Y8RN+60k=", "owner": "pyproject-nix", "repo": "pyproject.nix", - "rev": "84c4ea102127c77058ea1ed7be7300261fafc7d2", + "rev": "c372512cad744dbdeb65dba864376a0a5ab339a7", "type": "github" }, "original": { @@ -401,11 +401,11 @@ ] }, "locked": { - "lastModified": 1760402624, - "narHash": "sha256-jF6UKLs2uGc2rtved8Vrt58oTWjTQoAssuYs/0578Z4=", + "lastModified": 1763716960, + "narHash": "sha256-PUlomle4klGbnZr0wOn8z61Mbt7tXh6Yp3hZ9/CQkq0=", "owner": "pyproject-nix", "repo": "pyproject.nix", - "rev": "84c4ea102127c77058ea1ed7be7300261fafc7d2", + "rev": "d6c61dbe0be75e2f4cf0efcdc62428175be4cfb5", "type": "github" }, "original": { @@ -461,11 +461,11 @@ ] }, "locked": { - "lastModified": 1762137611, - "narHash": "sha256-sTqb10FR/YQCuGbw16qxliX0NFlYg6evSEjN8w+9IYE=", + "lastModified": 1769091129, + "narHash": "sha256-Jj/vIHjiu4OdDIrDXZ3xOPCJrMZZKzhE2UIVXV/NYzY=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "3a0ebe5d2965692f990cb27e62f501ad35e3deeb", + "rev": "131e22d6a6d54ab72aeef6a5a661ab7005b4c596", "type": "github" }, "original": { @@ -529,11 +529,11 @@ ] }, "locked": { - "lastModified": 1761311587, - "narHash": "sha256-Msq86cR5SjozQGCnC6H8C+0cD4rnx91BPltZ9KK613Y=", + "lastModified": 1768158989, + "narHash": "sha256-67vyT1+xClLldnumAzCTBvU0jLZ1YBcf4vANRWP3+Ak=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "2eddae033e4e74bf581c2d1dfa101f9033dbd2dc", + "rev": "e96d59dff5c0d7fddb9d113ba108f03c3ef99eca", "type": "github" }, "original": { @@ -575,11 +575,11 @@ "pyproject-nix": "pyproject-nix_3" }, "locked": { - "lastModified": 1761872265, - "narHash": "sha256-i25GRgp2vUOebY70L3NTAgkd+Pr1hnn5xM3qHxH0ONU=", + "lastModified": 1768526251, + "narHash": "sha256-uT0ICn7tknFtdOP7zhkgOkusq38EcttqUCS5Ho7nIvc=", "owner": "pyproject-nix", "repo": "uv2nix", - "rev": "74dfb62871be152ad3673b143b0cc56105a4f3c5", + "rev": "1b8f4d4c874f909de24639142c1141c84119b0f0", "type": "github" }, "original": { diff --git a/nix/devShell.nix b/nix/devShell.nix index 1fe818760..45a670c45 100644 --- a/nix/devShell.nix +++ b/nix/devShell.nix @@ -101,10 +101,7 @@ echo "Welcome to the Psyche development shell."; ''; }; - in - { - default = craneLib.devShell defaultShell; - dev-python = craneLib.devShell ( + pythonShell = craneLib.devShell ( defaultShell // { packages = defaultShell.packages ++ [ @@ -119,6 +116,11 @@ ''; } ); + in + { + default = craneLib.devShell defaultShell; + python = pythonShell; + dev-python = pythonShell; # old name, kept for backwards compatibility }; }; } diff --git a/nix/docker.nix b/nix/docker.nix index 60e00ebde..acbea1ea5 100644 --- a/nix/docker.nix +++ b/nix/docker.nix @@ -1,5 +1,6 @@ { pkgs, + lib ? pkgs.lib, rustPackages, inputs, externalRustPackages, @@ -204,4 +205,4 @@ let }; }; in -if pkgs.stdenv.isLinux then dockerPackages else { } +lib.optionalAttrs pkgs.stdenv.isLinux dockerPackages diff --git a/nix/lib.nix b/nix/lib.nix index 961d212ad..d45fac7f8 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -5,6 +5,8 @@ gitcommit ? inputs.self.rev or inputs.self.dirtyRev or "unknown", }: let + optionalApply = cond: f: if cond then f else lib.id; + util = import ./util.nix; system = pkgs.stdenv.hostPlatform.system; rustToolchain = pkgs.rust-bin.stable.latest.default.override { @@ -102,135 +104,372 @@ let } // pythonDeps ); - - buildRustPackageWithPsychePythonEnvironment = - { - name, - isExample ? false, - }: - let - rustPackage = craneLib.buildPackage ( - rustWorkspaceArgsWithPython - // { - inherit cargoArtifacts; - pname = name; - cargoExtraArgs = - rustWorkspaceArgsWithPython.cargoExtraArgs - + (if isExample then " --example ${name}" else " --bin ${name}"); - doCheck = false; - - meta.mainProgram = name; - } - ); - in - pkgs.runCommand "${name}" - { - buildInputs = [ pkgs.makeWrapper ]; - meta.mainProgram = name; - } - '' - mkdir -p $out/bin - makeWrapper ${rustPackage}/bin/${name} $out/bin/${name} \ - --prefix PATH : "${psychePythonVenvWithExtension}/bin" - ''; - - buildRustPackageWithoutPython = - { - name, - isExample ? false, - }: - craneLib.buildPackage ( - rustWorkspaceArgsNoPython - // { - cargoArtifacts = cargoArtifactsNoPython; - pname = name; - cargoExtraArgs = - rustWorkspaceArgsNoPython.cargoExtraArgs - + (if isExample then " --example ${name}" else " --bin ${name}"); - doCheck = false; - - meta.mainProgram = name; - } - ); - # builds a rust package # Returns an attrset of packages: { packageName = ...; packageName-nopython = ...; } # Automatically discovers and builds examples from the crate's examples/ directory + # Automatically discovers and builds integration tests from the crate's tests/ directory # Auto-detects if package has a main binary by checking for src/main.rs or src/bin/ # needsPython: true = only with Python + ext, false = only without Python + ext, "optional" = both variants # needsGpu: wraps the package with nix-gl-host + # supportedSystems: list of systems to build on (e.g., [ "x86_64-linux" "aarch64-linux" ]), null means all systems + # buildInputs: attrset of runtime dependencies for each built binary + # - buildInputs.main = [ deps ] applies to src/main.rs + # - buildInputs. = [ deps ] applies to all binaries of type (bin/test/example) + # - buildInputs.. = [ deps ] applies to specific binary buildRustPackage = { needsPython ? false, needsGpu ? false, - isExample ? false, cratePath, # path to the crate dir + supportedSystems ? null, + buildInputs ? { }, }: let - actualPath = if cratePath != null then cratePath else ./.; - - cargoToml = builtins.fromTOML (builtins.readFile (actualPath + "/Cargo.toml")); - packageName = cargoToml.package.name; - - # Auto-detect if this crate has a binary - hasMainRs = builtins.pathExists (actualPath + "/src/main.rs"); - hasBinDir = builtins.pathExists (actualPath + "/src/bin"); - hasBinary = hasMainRs || hasBinDir; + # type: "main" | "bin" | "test" | "example" + # name: the binary / test name + getRuntimeDepsForArtifact = + type: name: + let + typeConfig = buildInputs.${type} or null; + + # there's only one "main" + mainDeps = + if type == "main" then + ( + if lib.isList typeConfig then + typeConfig + else if typeConfig != null then + throw "buildInputs.main must be a list, got ${builtins.typeOf typeConfig}" + else + [ ] + ) + else + [ ]; + + # for other types, take both list (all bins) and attrset (specific bin) + typeDeps = if type != "main" then (if lib.isList typeConfig then typeConfig else [ ]) else [ ]; + + specificDeps = + if type != "main" && lib.isAttrs typeConfig then (typeConfig.${name} or [ ]) else [ ]; + in + mainDeps ++ typeDeps ++ specificDeps; + + buildMaybePythonRustPackage = + { + name, + type, + withPython, + originalName ? name, + dir ? null, + }: + assert lib.assertMsg (builtins.elem type [ + "bin" + "test" + "example" + ]) "type must be 'bin', 'test', or 'example', got: ${type}"; + let + workspaceArgs = if withPython then rustWorkspaceArgsWithPython else rustWorkspaceArgsNoPython; + artifacts = if withPython then cargoArtifacts else cargoArtifactsNoPython; - buildVariants = - name: withPython: withoutPython: + # delete conflicting bins from other crates to prevent ambiguous --bin/--example/--test + deleteConflicts = + let + # main binaries use packageName, others use originalName + binaryName = if type == "bin" && dir == null then packageName else originalName; + # main binaries (dir==null) delete from src/bin, others use their dir + conflictingDir = if dir == null then "src/bin" else dir; + in + '' + echo "Resolving workspace binary conflicts for ${type} ${originalName} from crate ${packageName}" + + # get workspace root and this crate's manifest path from cargo metadata + crate_manifest=$(cargo metadata --format-version 1 --no-deps | \ + jq -r '.packages[] | select(.name == "${packageName}") | .manifest_path') + workspace_root=$(cargo metadata --format-version 1 --no-deps | jq -r '.workspace_root') + crate_dir=$(dirname "$crate_manifest") + crate_relative_path=$(realpath --relative-to="$workspace_root" "$crate_dir") + + ${lib.optionalString (type == "bin" && dir == null) '' + # validate no conflicting src/bin/${packageName}.rs in same crate + if [ -f "$crate_relative_path/src/main.rs" ] && [ -f "$crate_relative_path/${conflictingDir}/${binaryName}.rs" ]; then + echo "Error: crate ${packageName} has both src/main.rs and src/bin/${packageName}.rs" + echo "This makes '--bin ${packageName}' ambiguous." + exit 1 + fi + ''} + + # delete conflicting files from all workspace members + while IFS= read -r member_id; do + member_manifest=$(cargo metadata --format-version 1 --no-deps | \ + jq -r ".packages[] | select(.id == \"$member_id\") | .manifest_path") + member_name=$(cargo metadata --format-version 1 --no-deps | \ + jq -r ".packages[] | select(.id == \"$member_id\") | .name") + member_dir=$(dirname "$member_manifest") + member_relative=$(realpath --relative-to="$workspace_root" "$member_dir") + + # skip our own crate + if [ "$member_relative" != "$crate_relative_path" ]; then + conflict_file="$member_relative/${conflictingDir}/${binaryName}.rs" + if [ -f "$conflict_file" ]; then + echo "Deleting conflicting file: $conflict_file" + rm "$conflict_file" + fi + fi + + ${lib.optionalString (type == "bin" && dir == "src/bin") '' + # delete src/main.rs if package name matches binary name + if [ "$member_name" = "${originalName}" ]; then + conflict_file="$member_relative/src/main.rs" + if [ -f "$conflict_file" ]; then + echo "Deleting conflicting file: $conflict_file (package name conflicts with binary name)" + rm "$conflict_file" + fi + fi + ''} + done < <(cargo metadata --format-version 1 --no-deps | jq -r '.workspace_members[]') + ''; + + rustPackage = craneLib.buildPackage ( + workspaceArgs + // { + cargoArtifacts = artifacts; + pname = name; + cargoExtraArgs = workspaceArgs.cargoExtraArgs + " --${type} ${originalName}"; + doCheck = false; + meta.mainProgram = name; + + # delete conflicting files from other crates to avoid ambiguity in what to build + preBuild = deleteConflicts; + nativeBuildInputs = workspaceArgs.nativeBuildInputs ++ [ pkgs.jq ]; + + doInstallCargoArtifacts = false; + installPhase = '' + runHook preInstall + mkdir -p $out/bin + + ${ + if type == "test" then + # tests have hash suffixes and live in deps/ + '' + expected_binary_dir="target/release/deps" + built_binary=$(find "$expected_binary_dir" -maxdepth 1 -name "${originalName}-*" -type f -executable | head -n1) + '' + else + # binaries and examples are in release/ with exact name + '' + expected_binary_dir="target/release" + built_binary="$expected_binary_dir/${originalName}" + '' + } + + if [ -n "$built_binary" ] && [ -f "$built_binary" ]; then + cp "$built_binary" $out/bin/${name} + chmod +x $out/bin/${name} + else + echo "Error: binary ${originalName} not found in $expected_binary_dir" + echo "Contents of $expected_binary_dir:" + ls -la "$expected_binary_dir/" || true + exit 1 + fi + + runHook postInstall + ''; + } + ); + + runtimeDeps = getRuntimeDepsForArtifact type originalName; + allRuntimeDeps = (lib.optionals withPython [ psychePythonVenvWithExtension ]) ++ runtimeDeps; + + wrappedRustPackage = + pkgs.runCommand "${name}" + { + buildInputs = [ pkgs.makeWrapper ]; + meta.mainProgram = name; + } + '' + mkdir -p $out/bin + makeWrapper ${rustPackage}/bin/${name} $out/bin/${name} \ + ${lib.concatMapStringsSep " " (dep: "--prefix PATH : \"${dep}/bin\"") allRuntimeDeps} + ''; + in + if allRuntimeDeps != [ ] then wrappedRustPackage else rustPackage; + + # build a target with python/nopython variants + buildTarget = + { + name, + originalName ? name, + type, + needsPython, + needsGpu, + dir ? null, + }: let - maybeWrapGpu = pkg: if needsGpu then useHostGpuDrivers pkg else pkg; + maybeWrapGpu = optionalApply needsGpu useHostGpuDrivers; + + mkVariant = + withPython: + maybeWrapGpu (buildMaybePythonRustPackage { + inherit + type + dir + name + originalName + withPython + ; + }); + + withPython = mkVariant true; + withoutPython = mkVariant false; + in if needsPython == "optional" then { - ${name} = maybeWrapGpu withPython; - "${name}-nopython" = maybeWrapGpu withoutPython; + ${name} = withPython; + "${name}-nopython" = withoutPython; } else if lib.isBool needsPython then - { - ${name} = maybeWrapGpu (if needsPython then withPython else withoutPython); - } + { ${name} = if needsPython then withPython else withoutPython; } else throw "needsPython must be true, false, or \"optional\", got: ${builtins.toString needsPython}"; - withPython = buildRustPackageWithPsychePythonEnvironment { + allRsFilenamesInDir = + dir: + let + entries = lib.optionalAttrs (builtins.pathExists dir) (builtins.readDir dir); + rustFiles = lib.filterAttrs (n: v: v == "regular" && lib.hasSuffix ".rs" n) entries; + in + lib.mapAttrsToList (name: _: lib.removeSuffix ".rs" name) rustFiles; + + buildTargetsFromDir = + { + dir, + type, + needsPython, + needsGpu, + prefix ? "", + }: + let + absoluteDir = cratePath + "/${dir}"; + targetNames = allRsFilenamesInDir absoluteDir; + buildOne = + name: + buildTarget { + inherit + type + needsPython + needsGpu + dir + ; + originalName = name; + name = "${prefix}${name}"; + }; + in + builtins.foldl' (acc: name: acc // (buildOne name)) { } targetNames; + + cargoToml = builtins.fromTOML (builtins.readFile (cratePath + "/Cargo.toml")); + packageName = cargoToml.package.name; + hasMainRs = builtins.pathExists (cratePath + "/src/main.rs"); + + # build src/main.rs if it exists (output is guaranteed unique by crate name) + mainRsPackage = lib.optionalAttrs hasMainRs (buildTarget { name = packageName; - inherit isExample; + type = "bin"; + inherit needsPython needsGpu; + }); + + binDirPackages = buildTargetsFromDir { + dir = "src/bin"; + type = "bin"; + prefix = "bin-${packageName}-"; + inherit needsPython needsGpu; }; - withoutPython = buildRustPackageWithoutPython { - name = packageName; - inherit isExample; + examplePackages = buildTargetsFromDir { + dir = "examples"; + type = "example"; + inherit needsPython needsGpu; }; - mainPackages = if !hasBinary then { } else buildVariants packageName withPython withoutPython; + testPackages = buildTargetsFromDir { + dir = "tests"; + type = "test"; + prefix = "test-${packageName}-"; + inherit needsPython needsGpu; + }; - examplesDir = actualPath + "/examples"; + shouldBuildForThisSystem = supportedSystems == null || builtins.elem system supportedSystems; - examplePackages = + validateBuildInputs = let - entries = if builtins.pathExists examplesDir then builtins.readDir examplesDir else { }; - exampleFiles = lib.filterAttrs (n: v: v == "regular" && lib.hasSuffix ".rs" n) entries; - exampleNames = lib.mapAttrsToList (name: _: lib.removeSuffix ".rs" name) exampleFiles; - - buildExample = - exampleName: + binNames = allRsFilenamesInDir (cratePath + "/src/bin"); + exampleNames = allRsFilenamesInDir (cratePath + "/examples"); + testNames = allRsFilenamesInDir (cratePath + "/tests"); + + availableArtifacts = { + main = lib.optional hasMainRs packageName; + bin = binNames; + example = exampleNames; + test = testNames; + }; + + # Check each buildInputs key + checkType = + type: let - build = - builder: - builder { - name = exampleName; - isExample = true; - }; - withPythonExample = build buildRustPackageWithPsychePythonEnvironment; - withoutPythonExample = build buildRustPackageWithoutPython; + typeConfig = buildInputs.${type} or null; + available = availableArtifacts.${type} or [ ]; in - buildVariants exampleName withPythonExample withoutPythonExample; + if typeConfig == null then + null + else if type == "main" then + # main must exist + if !hasMainRs then + throw "buildInputs.main specified but ${packageName} has no src/main.rs" + else + null + else if lib.isList typeConfig then + null # list format is always valid for a type + else if lib.isAttrs typeConfig then + # Check each specific artifact name + let + specifiedNames = lib.attrNames typeConfig; + invalidNames = lib.filter (name: !(lib.elem name available)) specifiedNames; + in + if invalidNames != [ ] then + throw "buildInputs.${type} specifies non-existent artifacts: ${lib.concatStringsSep ", " invalidNames}. Available ${type} artifacts: ${lib.concatStringsSep ", " available}" + else + null + else + throw "buildInputs.${type} must be a list or attrset, got ${builtins.typeOf typeConfig}"; + + invalidTypes = lib.filter ( + t: + !(lib.elem t [ + "main" + "bin" + "example" + "test" + ]) + ) (lib.attrNames buildInputs); + typeChecks = lib.map checkType (lib.attrNames buildInputs); in - builtins.foldl' (acc: exampleName: acc // (buildExample exampleName)) { } exampleNames; + x: + if invalidTypes != [ ] then + throw "buildInputs has invalid types: ${lib.concatStringsSep ", " invalidTypes}. Valid types: main, bin, example, test" + else + x; in - mainPackages // examplePackages; + validateBuildInputs ( + lib.optionalAttrs shouldBuildForThisSystem ( + util.mergeAttrsetsNoConflicts "can't merge binary package sets" [ + mainRsPackage + binDirPackages + examplePackages + testPackages + ] + ) + ); # TODO: i can't set the rust build target to WASM for the build deps for wasm-pack, since *some* of them don't build. # really, i want like a wasm-only set of deps to build... can I do that? @@ -284,30 +523,26 @@ let } ); - useHostGpuDrivers = - if pkgs.config.cudaSupport then - ( - package: - assert lib.assertMsg ( - package.meta ? mainProgram - ) "Package ${package.name} must have meta.mainProgram set to use useHostGpuDrivers"; - pkgs.runCommand "${package.name}-nixgl-wrapped" - { - nativeBuildInputs = [ pkgs.makeWrapper ]; - meta.mainProgram = package.meta.mainProgram; - } - '' - mkdir -p $out/bin - for bin in ${package}/bin/*; do - if [ -f "$bin" ] && [ -x "$bin" ]; then - makeWrapper "$bin" "$out/bin/$(basename $bin)" \ - --run 'exec ${pkgs.nix-gl-host}/bin/nixglhost "'"$bin"'" -- "$@"' - fi - done - '' - ) - else - (package: package); + useHostGpuDrivers = optionalApply pkgs.config.cudaSupport ( + package: + assert lib.assertMsg ( + package.meta ? mainProgram + ) "Package ${package.name} must have meta.mainProgram set to use useHostGpuDrivers"; + pkgs.runCommand "${package.name}-nixgl-wrapped" + { + nativeBuildInputs = [ pkgs.makeWrapper ]; + meta.mainProgram = package.meta.mainProgram; + } + '' + mkdir -p $out/bin + for bin in ${package}/bin/*; do + if [ -f "$bin" ] && [ -x "$bin" ]; then + makeWrapper "$bin" "$out/bin/$(basename $bin)" \ + --run 'exec ${pkgs.nix-gl-host}/bin/nixglhost "'"$bin"'" -- "$@"' + fi + done + '' + ); solanaCraneLib = (inputs.crane.mkLib pkgs).overrideToolchain @@ -387,8 +622,6 @@ in rustWorkspaceArgs rustWorkspaceArgsWithPython cargoArtifacts - buildRustPackageWithPsychePythonEnvironment - buildRustPackageWithoutPython buildRustPackage buildRustWasmTsPackage useHostGpuDrivers diff --git a/nix/nixpkgs.nix b/nix/nixpkgs.nix index 61d3c5b6b..c1f9a070d 100644 --- a/nix/nixpkgs.nix +++ b/nix/nixpkgs.nix @@ -15,13 +15,27 @@ in overlays = lib.optionals cudaSupported [ inputs.nix-gl-host.overlays.default ] ++ [ inputs.rust-overlay.overlays.default (final: prev: { + # temporary fix until https://github.com/NixOS/nixpkgs/pull/471394 is merged + # that lets us use `torch` instead of `torch-bin` + cudaPackages = prev.cudaPackages // { + cudnn = prev.cudaPackages.cudnn.overrideAttrs (old: { + appendRunpaths = (old.appendRunpaths or [ ]) ++ [ + "${prev.lib.getLib prev.cudaPackages.cuda_nvrtc}/lib" + ]; + }); + }; + + # provide packages for uv2pip to include python312Packages = prev.python312Packages.override { overrides = pyfinal: pyprev: { - torch = pyfinal.torch-bin; flash-attn = pyfinal.callPackage ../python/flash-attn.nix { }; liger-kernel = pyfinal.callPackage ../python/liger-kernel.nix { }; torchtitan = pyfinal.callPackage ../python/torchtitan.nix { }; torchdata = pyfinal.callPackage ../python/torchdata.nix { }; + keras = pyprev.keras.overrideAttrs (oldAttrs: { + dontUsePytestCheck = "please dont"; + + }); }; }; }) diff --git a/nix/pkgs.nix b/nix/pkgs.nix index e4673ce13..29bfb1b16 100644 --- a/nix/pkgs.nix +++ b/nix/pkgs.nix @@ -9,6 +9,7 @@ lib.makeScope pkgs.newScope ( psycheLib = import ./lib.nix { inherit pkgs inputs; }; + util = import ./util.nix; workspaceCargoToml = builtins.fromTOML (builtins.readFile ../Cargo.toml); @@ -86,8 +87,8 @@ lib.makeScope pkgs.newScope ( }; # a packages.nix returns an attrset of packages (including examples) - rustPackages = mergeAttrsetsNoConflicts "can't merge rust package sets." ( - lib.map (pkg: import (pkg.path + "/packages.nix") { inherit psycheLib; }) ( + rustPackages = util.mergeAttrsetsNoConflicts "can't merge rust package sets." ( + lib.map (pkg: import (pkg.path + "/packages.nix") { inherit psycheLib pkgs inputs; }) ( discoverCratesWithPackagesNix expandedMembers ) ); @@ -101,21 +102,8 @@ lib.makeScope pkgs.newScope ( ; }; - mergeAttrsetsNoConflicts = - error: attrsets: - builtins.foldl' ( - acc: current: - let - conflicts = builtins.filter (key: builtins.hasAttr key acc) (builtins.attrNames current); - in - if conflicts != [ ] then - throw "${error} Conflicting keys: ${builtins.toString conflicts}" - else - acc // current - ) { } attrsets; - psychePackages = ( - mergeAttrsetsNoConflicts "can't merge psyche package sets." [ + util.mergeAttrsetsNoConflicts "can't merge psyche package sets." [ { psyche-website-shared = self.callPackage ../website/shared { }; diff --git a/nix/python.nix b/nix/python.nix index 16f09970c..b24335d7a 100644 --- a/nix/python.nix +++ b/nix/python.nix @@ -33,6 +33,7 @@ let "torch" ] ++ lib.optionals stdenvNoCC.hostPlatform.isLinux [ + "vllm" # for inference package "flash-attn" "liger-kernel" # i'm really not a fan of providing torchtitan like this. i'd much rather have it be built as a git dep via uv2nix. diff --git a/nix/util.nix b/nix/util.nix new file mode 100644 index 000000000..9d40bfe28 --- /dev/null +++ b/nix/util.nix @@ -0,0 +1,14 @@ +{ + mergeAttrsetsNoConflicts = + error: attrsets: + builtins.foldl' ( + acc: current: + let + conflicts = builtins.filter (key: builtins.hasAttr key acc) (builtins.attrNames current); + in + if conflicts != [ ] then + throw "${error} Conflicting keys: ${builtins.toString conflicts}" + else + acc // current + ) { } attrsets; +} diff --git a/psyche-book/book.toml b/psyche-book/book.toml index b15598f64..697126b5b 100644 --- a/psyche-book/book.toml +++ b/psyche-book/book.toml @@ -1,7 +1,6 @@ [book] authors = ["Nous Research"] language = "en" -multilingual = false src = "src" title = "Psyche" diff --git a/psyche-book/default.nix b/psyche-book/default.nix index 0bf0e4a34..9eff7d91c 100644 --- a/psyche-book/default.nix +++ b/psyche-book/default.nix @@ -1,50 +1,14 @@ { lib, - mdbook, - fetchFromGitHub, - rustPlatform, stdenvNoCC, + mdbook, mdbook-mermaid, mdbook-linkcheck, + fetchFromGitHub, # custom args rustPackages, }: -let - mdbook-0-4-47 = mdbook.overrideAttrs ( - oldAttrs: - let - version = "0.4.47"; - src = fetchFromGitHub { - owner = "rust-lang"; - repo = "mdBook"; - tag = "v${version}"; - hash = "sha256-XTvC2pGRVat0kOybNb9TziG32wDVexnFx2ahmpUFmaA="; - }; - in - { - inherit version src; - cargoDeps = rustPlatform.fetchCargoVendor { - inherit (oldAttrs) pname; - inherit version src; - allowGitDependencies = false; - hash = "sha256-ASPRBAB+elJuyXpPQBm3WI97wD3mjoO1hw0fNHc+KAw="; - }; - } - ); -in -let - # Pull crate binary package names from rustPackages - # prefer -nopython suffix if available, otherwise use normal version - allPackageNames = builtins.attrNames rustPackages; - - binaryPackageNames = lib.unique ( - builtins.filter ( - name: - if lib.hasSuffix "-nopython" name then true else !(builtins.elem "${name}-nopython" allPackageNames) - ) allPackageNames - ); -in stdenvNoCC.mkDerivation { __structuredAttrs = true; @@ -52,9 +16,26 @@ stdenvNoCC.mkDerivation { src = ./.; nativeBuildInputs = [ - mdbook-0-4-47 + mdbook mdbook-mermaid - mdbook-linkcheck + (mdbook-linkcheck.overrideAttrs ( + final: prev: { + version = "unstable-2025-12-04"; + src = fetchFromGitHub { + owner = "schilkp"; + repo = "mdbook-linkcheck"; + rev = "ed981be6ded11562e604fff290ae4c08f1c419c5"; + sha256 = "sha256-GTVWc/vkqY9Hml2fmm3iCHOzd/HPP1i/8NIIjFqGGbQ="; + }; + + cargoDeps = prev.cargoDeps.overrideAttrs (previousAttrs: { + vendorStaging = previousAttrs.vendorStaging.overrideAttrs { + inherit (final) src; + outputHash = "sha256-+73aI/jt5mu6dR6PR9Q08hPdOsWukb/z9crIdMMeF7U="; + }; + }); + } + )) ]; postPatch = '' @@ -63,13 +44,22 @@ stdenvNoCC.mkDerivation { # we set HOME to a writable directory to avoid cache dir permission issues export HOME=$TMPDIR - ${lib.concatMapStringsSep "\n" ( - name: - let - basename = lib.replaceStrings [ "-nopython" ] [ "" ] name; - in - "${rustPackages.${name}}/bin/${basename} print-all-help --markdown > generated/cli/${basename}.md" - ) binaryPackageNames} + ${lib.concatMapStringsSep "\n" + ( + name: + let + basename = lib.replaceStrings [ "-nopython" ] [ "" ] name; + in + "${rustPackages.${name}}/bin/${basename} print-all-help --markdown > generated/cli/${basename}.md" + ) + [ + "psyche-centralized-local-testnet" + "psyche-sidecar" + "psyche-centralized-client" + "psyche-centralized-server" + "psyche-solana-client" + ] + } cp ${../secrets.nix} generated/secrets.nix ''; diff --git a/psyche-book/src/development/agenix.md b/psyche-book/src/development/agenix.md index 56eff7b22..dbda95634 100644 --- a/psyche-book/src/development/agenix.md +++ b/psyche-book/src/development/agenix.md @@ -9,7 +9,7 @@ You can read more about agenix and how secrets are used in our deployment [HERE] ## What secrets do we store? ```nix -{{#include ../../../secrets.nix}} +{{#include ../../generated/secrets.nix}} ``` ## Editing a secret diff --git a/psyche-book/src/development/python.md b/psyche-book/src/development/python.md index 62339776d..aaad835a4 100644 --- a/psyche-book/src/development/python.md +++ b/psyche-book/src/development/python.md @@ -24,7 +24,7 @@ This shell provides: ### Development Workflow -You can use `uv pip` to install arbitrary packages. Dependencies are tracked via `uv.lock`, so if you don't have `direnv` set up, you must exit and re-enter the development shell with `nix develop .#dev-python`. +You can use `uv pip` to install arbitrary packages. Dependencies are tracked via `uv.lock`, so if you don't have `direnv` set up, you must exit and re-enter the development shell with `nix develop .#python`. When you enter the dev shell, it compiles the Rust extension that provides the `psyche` Python module. **If you modify any Rust code in the Python extension or its dependencies, you must exit and re-enter the dev shell** to recompile the extension. @@ -33,7 +33,7 @@ We recommend running commands directly through the dev shell without entering it For example, to run the `train` program using python: ```bash -nix develop .#dev-python --command just train-model-python \ +nix develop .#python --command just train-model-python \ --model emozilla/llama2-20m-init \ --data-path ./data/fineweb-10bt/ \ --total-batch 2 \ @@ -44,7 +44,7 @@ nix develop .#dev-python --command just train-model-python \ Alternatively, you _could_ enter the shell and run the commands with: ```bash -nix develop .#dev-python +nix develop .#python ``` but **this is likely to be a footgun** as it's easy to forget to exit and re-enter the shell. diff --git a/psyche-book/theme/index.hbs b/psyche-book/theme/index.hbs index 373287457..c8d98fe91 100644 --- a/psyche-book/theme/index.hbs +++ b/psyche-book/theme/index.hbs @@ -33,15 +33,12 @@ {{/if}} - - {{#if copy_fonts}} - {{/if}} - - - + + + {{#each additional_css}} @@ -53,21 +50,38 @@ {{/if}} - + -
+
+
+

Keyboard shortcuts

+
+

Press or to navigate between chapters

+ {{#if search_enabled}} +

Press S or / to search in the book

+ {{/if}} +

Press ? to show this help

+

Press Esc to hide this help

+
+
+
+
- + -