starforge-core 0.3.2__tar.gz → 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {starforge_core-0.3.2 → starforge_core-0.3.4}/.gitignore +2 -0
- starforge_core-0.3.4/PKG-INFO +95 -0
- starforge_core-0.3.4/README.md +65 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/pyproject.toml +2 -1
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/__init__.py +1 -1
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/exp.py +2 -3
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/submit.py +4 -7
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/model.py +113 -1
- starforge_core-0.3.4/tests/cli/conftest.py +19 -0
- starforge_core-0.3.4/tests/cli/test_bench.py +78 -0
- starforge_core-0.3.4/tests/cli/test_catalog_handshake.py +130 -0
- starforge_core-0.3.4/tests/cli/test_cli.py +381 -0
- starforge_core-0.3.4/tests/cli/test_cli_login.py +219 -0
- starforge_core-0.3.4/tests/cli/test_cli_ui.py +46 -0
- starforge_core-0.3.4/tests/cli/test_config_resolve.py +114 -0
- starforge_core-0.3.4/tests/cli/test_maintenance_cli.py +116 -0
- starforge_core-0.3.4/tests/cli/test_new_experiment.py +172 -0
- starforge_core-0.3.4/tests/cli/test_recipe_lock.py +105 -0
- starforge_core-0.3.4/tests/cli/test_repo.py +168 -0
- starforge_core-0.3.4/tests/cli/test_spec_builder.py +383 -0
- starforge_core-0.3.4/tests/cli/test_submit_pack.py +283 -0
- starforge_core-0.3.4/tests/cli/test_sweep.py +153 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_recipes.py +78 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/uv.lock +1 -1
- starforge_core-0.3.2/PKG-INFO +0 -28
- {starforge_core-0.3.2 → starforge_core-0.3.4}/gitlab-ci.example.yml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/pytest.ini +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/scripts/build_and_check.sh +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/artifacts.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/benchmarks/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/benchmarks/runners.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/bootstrap.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/capsule.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/capsule_runner.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/api_client.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/auth.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/catalog.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/cli.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/cli_ui.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/client_device.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/admin.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/bench.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/common.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/corpus.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/dataset.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/init.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/jobs.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/login.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/plugin.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/recipe.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/serve.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/commands/sweep.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/config_resolve.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/data_prep.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/new_experiment.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/packing.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/plugins_lock.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/recipe_lock.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/repo.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/agent-run.py.tmpl +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/custom-framework/train.sh +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/experiment-template/.gitkeep +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/experiment-template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/experiment-template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/common/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/common/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/distillation_math.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/grpo_lora.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/grpo_math_1B.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/grpo_megatron.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/grpo_noncolocated.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/grpo_sliding_puzzle.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/ppo_math_1B.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/rm.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/base/sft.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/models/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/models/qwen3.5-4b.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/configs/models/qwen3.5-9b.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/scaffold/project/gitignore +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/cli/spec_builder.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/config_resolve.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/contract/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/contract/binding.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/contract/env.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/contract/errors.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/contract/names.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/contract/spec.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/contract/topology.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/datasets.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/distributed/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/distributed/coordinator.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/distributed/protocol.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/distributed/ray_runtime.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/fatal_logs.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/actions.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/base.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/custom.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/evalkit.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/nemo_rl.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/observability.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/openrlhf.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/registry.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/runtime.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/trl.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/frameworks/verl.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/launcher.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/lifecycle/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/lifecycle/benchmark_eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/lifecycle/export_complete.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/lifecycle/nemo_eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/lifecycle/nemo_export.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/lifecycle/verl_eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/log_forward.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/log_normalize.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/metrics.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/observability/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/observability/env_probe.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/observability/hardware_monitor.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/observability/hw_probe.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/observability/job_nodes.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/observability/sampling.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/observability/sink.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/observability/util.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/package_bootstrap.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/pkce.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/plugins.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/__init__.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/custom/custom/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/custom/custom/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/custom/custom/template/train.sh +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/evalkit/benchmark/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/evalkit/benchmark/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/distillation/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/distillation/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/distillation/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/dpo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/dpo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/dpo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/grpo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/grpo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/grpo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/grpo-lora/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/grpo-lora/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/grpo-lora/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/maxrl/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/maxrl/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/maxrl/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/maxrl/template/run.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/opsd/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/opsd/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/opsd/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/opsd/template/run.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/ppo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/ppo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/ppo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/rm/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/rm/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/rm/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/sft/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/sft/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/nemo-rl/sft/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/dpo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/dpo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/dpo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/dpo/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/grpo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/grpo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/grpo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/grpo/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/ppo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/ppo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/ppo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/ppo/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/reinforce-baseline/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/reinforce-baseline/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/reinforce-baseline/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/reinforce-baseline/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/rm/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/rm/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/rm/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/rm/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/sft/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/sft/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/sft/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/openrlhf/sft/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/dpo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/dpo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/dpo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/dpo/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/dpo/template/train.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/grpo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/grpo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/grpo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/grpo/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/grpo/template/train.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/kto/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/kto/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/kto/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/kto/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/kto/template/train.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/opsd/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/opsd/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/opsd/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/opsd/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/opsd/template/train.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rloo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rloo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rloo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rloo/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rloo/template/train.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rm/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rm/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rm/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rm/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/rm/template/train.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/sft/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/sft/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/sft/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/sft/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/trl/sft/template/train.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/verl/grpo/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/verl/grpo/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/verl/grpo/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/verl/grpo/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/verl/sft/recipe.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/verl/sft/template/README.md +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/verl/sft/template/config.yaml +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/catalog/verl/sft/template/eval.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/recipes/duration.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/report.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/reporter.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/sandbox.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/starforge/verl_ipc.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_architecture_boundaries.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_artifacts.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_bootstrap.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_capsule.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_config_validate.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_contract_env_golden.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_contract_spec.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_contract_topology.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_custom_adapter.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_datasets.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_distributed_coordinator.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_env_nodes_probe.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_env_probe.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_evalkit.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_fatal_logs.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_framework_adapters.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_framework_observability.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_framework_runtime.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_hw_probe.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_image_fingerprint.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_job_hw_scope.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_job_nodes.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_launcher.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_lifecycle_commands.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_log_forward.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_log_normalize.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_metrics.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_nemo_rl_adapter.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_observability_util.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_openrlhf_adapter.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_package_data.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_process_group_plan.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_report.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_reporter.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_runner.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_runtime_artifacts.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_sandbox.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_training_duration.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_trl_adapter.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_verl_adapter.py +0 -0
- {starforge_core-0.3.2 → starforge_core-0.3.4}/tests/test_verl_ipc.py +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: starforge-core
|
|
3
|
+
Version: 0.3.4
|
|
4
|
+
Summary: StarForge client: JobSpec / recipe contract, job-side runner, and sf CLI.
|
|
5
|
+
Project-URL: Homepage, https://github.com/wccdev/starforge
|
|
6
|
+
Project-URL: Documentation, https://github.com/wccdev/starforge
|
|
7
|
+
Project-URL: Repository, https://github.com/wccdev/starforge
|
|
8
|
+
Keywords: grpo,llm,post-training,rlhf,sft,starforge
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: packaging>=24
|
|
20
|
+
Requires-Dist: pyyaml>=6
|
|
21
|
+
Requires-Dist: typer>=0.12
|
|
22
|
+
Provides-Extra: data
|
|
23
|
+
Requires-Dist: datasets>=2.18; extra == 'data'
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: fastapi>=0.110; extra == 'dev'
|
|
26
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# starforge-core
|
|
32
|
+
|
|
33
|
+
Client for StarForge. You get the `sf` command and a Python package named `starforge`.
|
|
34
|
+
|
|
35
|
+
Use this to scaffold a lab, lock a post-training method, and submit jobs to a StarForge console. The control plane is a separate install, `starforge-server`. Training images do not need this whole package; the server injects a runner that carries the kernel only.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install starforge-core
|
|
41
|
+
# or
|
|
42
|
+
uv tool install starforge-core
|
|
43
|
+
|
|
44
|
+
sf --help
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Python 3.10 or newer. Import name is `starforge`, not `starforge_core`.
|
|
48
|
+
|
|
49
|
+
`pip install starforge-core[data]` adds HuggingFace `datasets` for local prepare scripts.
|
|
50
|
+
|
|
51
|
+
## Start a lab
|
|
52
|
+
|
|
53
|
+
Do not clone the platform repo. This package writes a project for you:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
sf init my-lab --yes
|
|
57
|
+
cd my-lab
|
|
58
|
+
sf login --server https://<your-console>
|
|
59
|
+
sf new my-grpo --method nemo-rl/grpo
|
|
60
|
+
sf validate my-grpo
|
|
61
|
+
sf submit my-grpo --profile h200:8
|
|
62
|
+
sf job logs
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`sf methods` lists the catalog on the server you logged into. Methods look like `nemo-rl/grpo`, `verl/sft`, `trl/kto`, `openrlhf/ppo`, `evalkit/benchmark`, `custom/custom`.
|
|
66
|
+
|
|
67
|
+
Examples live in `starforge-tutorial`. Day-to-day work should be your own `sf init` tree.
|
|
68
|
+
|
|
69
|
+
## Python
|
|
70
|
+
|
|
71
|
+
Catalog methods already forward framework logs. Custom `train.sh` jobs that want console curves call:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from starforge.report import init, log, finish
|
|
75
|
+
|
|
76
|
+
init()
|
|
77
|
+
log({"loss": 0.12}, step=1)
|
|
78
|
+
finish()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`starforge.spec_to_env` is the JobSpec to environment mapping used on both the client and the job. `starforge.get_recipe` / `starforge.recipe_names` read the bundled catalog.
|
|
82
|
+
|
|
83
|
+
## What this package contains
|
|
84
|
+
|
|
85
|
+
- `sf` CLI: init, login, experiments, submit, sweep, jobs, datasets, plugins, bench, serve
|
|
86
|
+
- JobSpec types and `spec_to_env`
|
|
87
|
+
- Recipe catalog and framework adapters (NeMo-RL, verl, TRL, OpenRLHF, evalkit, custom)
|
|
88
|
+
- Job-side reporter (`starforge.report`)
|
|
89
|
+
|
|
90
|
+
Cluster credentials stay on the server. Your laptop keeps an access token under `~/.forge/`.
|
|
91
|
+
|
|
92
|
+
## Docs and source
|
|
93
|
+
|
|
94
|
+
- Platform repo: https://github.com/wccdev/starforge
|
|
95
|
+
- User docs: `mintlify/` in that repo (English and 简体中文)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# starforge-core
|
|
2
|
+
|
|
3
|
+
Client for StarForge. You get the `sf` command and a Python package named `starforge`.
|
|
4
|
+
|
|
5
|
+
Use this to scaffold a lab, lock a post-training method, and submit jobs to a StarForge console. The control plane is a separate install, `starforge-server`. Training images do not need this whole package; the server injects a runner that carries the kernel only.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install starforge-core
|
|
11
|
+
# or
|
|
12
|
+
uv tool install starforge-core
|
|
13
|
+
|
|
14
|
+
sf --help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Python 3.10 or newer. Import name is `starforge`, not `starforge_core`.
|
|
18
|
+
|
|
19
|
+
`pip install starforge-core[data]` adds HuggingFace `datasets` for local prepare scripts.
|
|
20
|
+
|
|
21
|
+
## Start a lab
|
|
22
|
+
|
|
23
|
+
Do not clone the platform repo. This package writes a project for you:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
sf init my-lab --yes
|
|
27
|
+
cd my-lab
|
|
28
|
+
sf login --server https://<your-console>
|
|
29
|
+
sf new my-grpo --method nemo-rl/grpo
|
|
30
|
+
sf validate my-grpo
|
|
31
|
+
sf submit my-grpo --profile h200:8
|
|
32
|
+
sf job logs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`sf methods` lists the catalog on the server you logged into. Methods look like `nemo-rl/grpo`, `verl/sft`, `trl/kto`, `openrlhf/ppo`, `evalkit/benchmark`, `custom/custom`.
|
|
36
|
+
|
|
37
|
+
Examples live in `starforge-tutorial`. Day-to-day work should be your own `sf init` tree.
|
|
38
|
+
|
|
39
|
+
## Python
|
|
40
|
+
|
|
41
|
+
Catalog methods already forward framework logs. Custom `train.sh` jobs that want console curves call:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from starforge.report import init, log, finish
|
|
45
|
+
|
|
46
|
+
init()
|
|
47
|
+
log({"loss": 0.12}, step=1)
|
|
48
|
+
finish()
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`starforge.spec_to_env` is the JobSpec to environment mapping used on both the client and the job. `starforge.get_recipe` / `starforge.recipe_names` read the bundled catalog.
|
|
52
|
+
|
|
53
|
+
## What this package contains
|
|
54
|
+
|
|
55
|
+
- `sf` CLI: init, login, experiments, submit, sweep, jobs, datasets, plugins, bench, serve
|
|
56
|
+
- JobSpec types and `spec_to_env`
|
|
57
|
+
- Recipe catalog and framework adapters (NeMo-RL, verl, TRL, OpenRLHF, evalkit, custom)
|
|
58
|
+
- Job-side reporter (`starforge.report`)
|
|
59
|
+
|
|
60
|
+
Cluster credentials stay on the server. Your laptop keeps an access token under `~/.forge/`.
|
|
61
|
+
|
|
62
|
+
## Docs and source
|
|
63
|
+
|
|
64
|
+
- Platform repo: https://github.com/wccdev/starforge
|
|
65
|
+
- User docs: `mintlify/` in that repo (English and 简体中文)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Researcher client: contract kernel + sf CLI. Control plane is starforge-server.
|
|
2
2
|
[project]
|
|
3
3
|
name = "starforge-core"
|
|
4
|
-
version = "0.3.
|
|
4
|
+
version = "0.3.4"
|
|
5
5
|
description = "StarForge client: JobSpec / recipe contract, job-side runner, and sf CLI."
|
|
6
|
+
readme = "README.md"
|
|
6
7
|
requires-python = ">=3.10"
|
|
7
8
|
keywords = ["llm", "post-training", "rlhf", "grpo", "sft", "starforge"]
|
|
8
9
|
classifiers = [
|
|
@@ -114,11 +114,10 @@ def _validate_exp_contents(exp_dir, recipe, *, repo_root=None) -> tuple[list[str
|
|
|
114
114
|
|
|
115
115
|
def _validate_exp(exp_path: str, recipe_override: str = "") -> tuple[list[str], list[str]]:
|
|
116
116
|
"""锁必须是当前 bundle,再跑 recipe 所属框架的 validator。"""
|
|
117
|
-
from starforge.contract import SpecError
|
|
118
|
-
from starforge.recipes import get_recipe
|
|
119
|
-
|
|
120
117
|
from starforge.cli.recipe_lock import validate_recipe_lock
|
|
121
118
|
from starforge.cli.spec_builder import infer_recipe
|
|
119
|
+
from starforge.contract import SpecError
|
|
120
|
+
from starforge.recipes import get_recipe
|
|
122
121
|
|
|
123
122
|
exp_dir = common.ROOT / exp_path
|
|
124
123
|
recipe_name = recipe_override.strip() or infer_recipe(exp_dir)
|
|
@@ -174,9 +174,8 @@ def _materialize_profile_or_exit(exprs: list[str]) -> tuple[str, list[str], list
|
|
|
174
174
|
series 与默认形状查服务端注册表——用户只说「哪种卡、几张」,
|
|
175
175
|
拓扑细节由注册表补齐,两边不可能写出互相矛盾的资源声明。
|
|
176
176
|
"""
|
|
177
|
-
from starforge.contract import SpecError
|
|
178
|
-
|
|
179
177
|
from starforge.cli import spec_builder
|
|
178
|
+
from starforge.contract import SpecError
|
|
180
179
|
|
|
181
180
|
try:
|
|
182
181
|
return spec_builder.materialize_pools(exprs, common.profile_registry())
|
|
@@ -229,9 +228,8 @@ def _build_spec_or_exit(exp_path: str, *, method, project, sets, pools, roles,
|
|
|
229
228
|
framework_version=None, image=None, provenance=None,
|
|
230
229
|
validate: bool):
|
|
231
230
|
"""构建强 JobSpec;缺少显式 recipe 时立即退出。"""
|
|
232
|
-
from starforge.contract import SpecError
|
|
233
|
-
|
|
234
231
|
from starforge.cli import spec_builder
|
|
232
|
+
from starforge.contract import SpecError
|
|
235
233
|
|
|
236
234
|
recipe = (method or "").strip() or spec_builder.infer_recipe(common.ROOT / exp_path)
|
|
237
235
|
if not recipe:
|
|
@@ -342,11 +340,10 @@ def _echo_upload_summary(res: dict) -> None:
|
|
|
342
340
|
def _submit_post(action: str, exp_path: str, profile: Optional[str], flags: list[str],
|
|
343
341
|
dry_run: bool, allow_dirty: bool) -> int:
|
|
344
342
|
"""构建并预编译训练后强契约,再通过统一 launcher 提交。"""
|
|
345
|
-
from starforge.frameworks import CompileRequest, compile_launch_plan
|
|
346
|
-
from starforge.recipes import get_recipe
|
|
347
|
-
|
|
348
343
|
from starforge.cli import spec_builder
|
|
349
344
|
from starforge.cli.recipe_lock import validate_recipe_lock
|
|
345
|
+
from starforge.frameworks import CompileRequest, compile_launch_plan
|
|
346
|
+
from starforge.recipes import get_recipe
|
|
350
347
|
|
|
351
348
|
recipe_name = spec_builder.infer_recipe(common.ROOT / exp_path)
|
|
352
349
|
if not recipe_name:
|
|
@@ -542,6 +542,77 @@ class Entrypoint:
|
|
|
542
542
|
return cls(kind=kind, value=value, experiment_override=override)
|
|
543
543
|
|
|
544
544
|
|
|
545
|
+
#: 指标方向:higher = 越高越好,lower = 越低越好,neutral = 两头都不好 / 无优劣。
|
|
546
|
+
METRIC_DIRECTIONS = ("higher", "lower", "neutral")
|
|
547
|
+
#: 展示形态:ratio = 0–1 的占比(展示为百分比,差值展示为百分点),number = 裸数值。
|
|
548
|
+
METRIC_DISPLAYS = ("ratio", "number")
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
@dataclass(frozen=True)
|
|
552
|
+
class MetricSemantics:
|
|
553
|
+
"""一个 canonical 指标「怎么读」:方向与展示形态。
|
|
554
|
+
|
|
555
|
+
这两件事此前只存在于 console 的键名正则里,而且是三份互不相同的正则 ——
|
|
556
|
+
`train/clip_frac` 在图表页按百分比画、在对比页按裸小数显示,同一个数字两种读法。
|
|
557
|
+
根因是契约声明了指标的**名字**却没声明它的**含义**,前端只能按名字猜。
|
|
558
|
+
"""
|
|
559
|
+
|
|
560
|
+
direction: str = "higher"
|
|
561
|
+
display: str = "number"
|
|
562
|
+
|
|
563
|
+
def to_dict(self) -> dict[str, str]:
|
|
564
|
+
return {"direction": self.direction, "display": self.display}
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
#: canonical 词表的内置语义。与 MetricContract docstring 里的词表逐条对应 ——
|
|
568
|
+
#: 新增 canonical 名时在这里补一行,recipe 无须逐个声明。
|
|
569
|
+
#:
|
|
570
|
+
#: 策略熵是 neutral 而不是 lower:RL 里熵归零就是策略塌缩、停止探索,判成 lower
|
|
571
|
+
#: 会让对比页把塌缩掉的那次训练标成最优;判成 higher 又会奖励纯随机策略。两头
|
|
572
|
+
#: 都不好的量就该说自己两头都不好,而不是被塞进一个二选一里。
|
|
573
|
+
CANONICAL_METRIC_SEMANTICS: dict[str, MetricSemantics] = {
|
|
574
|
+
"train/reward": MetricSemantics("higher", "number"),
|
|
575
|
+
"train/score": MetricSemantics("higher", "number"),
|
|
576
|
+
"train/loss": MetricSemantics("lower", "number"),
|
|
577
|
+
"train/kl_penalty": MetricSemantics("lower", "number"),
|
|
578
|
+
"train/entropy": MetricSemantics("neutral", "number"),
|
|
579
|
+
"train/grad_norm": MetricSemantics("lower", "number"),
|
|
580
|
+
"train/lr": MetricSemantics("neutral", "number"),
|
|
581
|
+
"train/response_length": MetricSemantics("neutral", "number"),
|
|
582
|
+
"train/clip_frac": MetricSemantics("lower", "ratio"),
|
|
583
|
+
"train/truncated_ratio": MetricSemantics("lower", "ratio"),
|
|
584
|
+
"train/reward_zero_std_frac": MetricSemantics("lower", "ratio"),
|
|
585
|
+
"train/value_loss": MetricSemantics("lower", "number"),
|
|
586
|
+
"train/value_explained_var": MetricSemantics("higher", "number"),
|
|
587
|
+
"train/accuracy": MetricSemantics("higher", "ratio"),
|
|
588
|
+
"train/token_accuracy": MetricSemantics("higher", "ratio"),
|
|
589
|
+
"train/margin": MetricSemantics("higher", "number"),
|
|
590
|
+
# 偏好对两侧的隐式奖励:绝对值无意义(看的是间隔 train/margin),
|
|
591
|
+
# 单独判优劣会把「两边一起涨」误读成进步。
|
|
592
|
+
"train/reward_chosen": MetricSemantics("neutral", "number"),
|
|
593
|
+
"train/reward_rejected": MetricSemantics("neutral", "number"),
|
|
594
|
+
"validation/accuracy": MetricSemantics("higher", "ratio"),
|
|
595
|
+
"validation/token_accuracy": MetricSemantics("higher", "ratio"),
|
|
596
|
+
"validation/loss": MetricSemantics("lower", "number"),
|
|
597
|
+
"validation/reward": MetricSemantics("higher", "number"),
|
|
598
|
+
"validation/margin": MetricSemantics("higher", "number"),
|
|
599
|
+
"validation/kl_penalty": MetricSemantics("lower", "number"),
|
|
600
|
+
"train/return": MetricSemantics("higher", "number"),
|
|
601
|
+
"train/token_mult_prob_error": MetricSemantics("lower", "number"),
|
|
602
|
+
# 优势/价值/长度这类量没有「越大越好」:优势均值应当在 0 附近(GAE 已中心化),
|
|
603
|
+
# critic 的 value 是对回报的估计而非目标,生成长度长短各有代价。
|
|
604
|
+
"train/advantages/mean": MetricSemantics("neutral", "number"),
|
|
605
|
+
"train/value": MetricSemantics("neutral", "number"),
|
|
606
|
+
"train/opsd_hint_len_mean": MetricSemantics("neutral", "number"),
|
|
607
|
+
"train/opsd_response_len_mean": MetricSemantics("neutral", "number"),
|
|
608
|
+
"train/opsd_kl_clip_frac": MetricSemantics("lower", "ratio"),
|
|
609
|
+
# 组内奖励标准差:归零是 GRPO 的失效信号(组内全同分,梯度为零),
|
|
610
|
+
# 过大则说明采样噪声压过了信号。同 entropy,两头都不好。
|
|
611
|
+
"train/reward_std": MetricSemantics("neutral", "number"),
|
|
612
|
+
"bench/score": MetricSemantics("higher", "number"),
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
|
|
545
616
|
@dataclass(frozen=True)
|
|
546
617
|
class MetricContract:
|
|
547
618
|
"""指标契约:把各框架的原生键名收敛到一套跨框架可比的 canonical 名字。
|
|
@@ -585,6 +656,23 @@ class MetricContract:
|
|
|
585
656
|
|
|
586
657
|
primary: tuple[str, ...]
|
|
587
658
|
aliases: dict[str, tuple[str, ...]]
|
|
659
|
+
#: 对 CANONICAL_METRIC_SEMANTICS 的按 recipe 覆写。绝大多数 recipe 用不上 ——
|
|
660
|
+
#: canonical 名字一样,含义就一样,那正是收敛到 canonical 词表的意义。
|
|
661
|
+
semantics: dict[str, MetricSemantics] = field(default_factory=dict)
|
|
662
|
+
|
|
663
|
+
def semantics_for(self, key: str) -> MetricSemantics:
|
|
664
|
+
"""该指标怎么读。未知键(含 `raw/` 前缀的未声明键)落到默认值。"""
|
|
665
|
+
if (own := self.semantics.get(key)) is not None:
|
|
666
|
+
return own
|
|
667
|
+
return CANONICAL_METRIC_SEMANTICS.get(key, MetricSemantics())
|
|
668
|
+
|
|
669
|
+
def semantics_map(self) -> dict[str, dict[str, str]]:
|
|
670
|
+
"""本契约全部 canonical 名的语义表 —— 服务端据此下发给 console。
|
|
671
|
+
|
|
672
|
+
canonical 名 = primary ∪ aliases 的键,与 starforge.metrics 的归一化口径同源。
|
|
673
|
+
"""
|
|
674
|
+
names = list(self.primary) + [k for k in self.aliases if k not in self.primary]
|
|
675
|
+
return {name: self.semantics_for(name).to_dict() for name in names}
|
|
588
676
|
|
|
589
677
|
@classmethod
|
|
590
678
|
def from_dict(cls, data: Mapping[str, Any] | None, *, recipe: str) -> MetricContract:
|
|
@@ -601,7 +689,26 @@ class MetricContract:
|
|
|
601
689
|
if isinstance(values, (str, bytes)) or not isinstance(values, Sequence):
|
|
602
690
|
raise SpecError("应为字符串数组", field=f"metrics.aliases.{canonical}")
|
|
603
691
|
aliases[str(canonical)] = tuple(str(v) for v in values)
|
|
604
|
-
|
|
692
|
+
semantics: dict[str, MetricSemantics] = {}
|
|
693
|
+
for canonical, raw in (data.get("semantics") or {}).items():
|
|
694
|
+
if not isinstance(raw, Mapping):
|
|
695
|
+
raise SpecError("应为对象(direction / display)", field=f"metrics.semantics.{canonical}")
|
|
696
|
+
direction = str(raw.get("direction", "higher"))
|
|
697
|
+
display = str(raw.get("display", "number"))
|
|
698
|
+
if direction not in METRIC_DIRECTIONS:
|
|
699
|
+
raise SpecError(
|
|
700
|
+
f"direction 必须是 {'/'.join(METRIC_DIRECTIONS)} 之一,得到 {direction!r}",
|
|
701
|
+
field=f"metrics.semantics.{canonical}.direction",
|
|
702
|
+
)
|
|
703
|
+
if display not in METRIC_DISPLAYS:
|
|
704
|
+
raise SpecError(
|
|
705
|
+
f"display 必须是 {'/'.join(METRIC_DISPLAYS)} 之一,得到 {display!r}",
|
|
706
|
+
field=f"metrics.semantics.{canonical}.display",
|
|
707
|
+
)
|
|
708
|
+
semantics[str(canonical)] = MetricSemantics(direction, display)
|
|
709
|
+
return cls(
|
|
710
|
+
primary=tuple(str(v) for v in primary_raw), aliases=aliases, semantics=semantics
|
|
711
|
+
)
|
|
605
712
|
|
|
606
713
|
|
|
607
714
|
#: 验证样本形态:conversation(RL 对话+奖励)/ preference(DPO 偏好对)/
|
|
@@ -924,6 +1031,11 @@ class Recipe:
|
|
|
924
1031
|
def metric_aliases(self) -> dict[str, tuple[str, ...]]:
|
|
925
1032
|
return dict(self.metrics.aliases)
|
|
926
1033
|
|
|
1034
|
+
@property
|
|
1035
|
+
def metric_semantics(self) -> dict[str, dict[str, str]]:
|
|
1036
|
+
"""本方法全部 canonical 指标的「怎么读」。服务端随 runtime_contract 下发。"""
|
|
1037
|
+
return self.metrics.semantics_map()
|
|
1038
|
+
|
|
927
1039
|
@property
|
|
928
1040
|
def max_steps_path(self) -> str | None:
|
|
929
1041
|
"""训练步数上限超参在配置树里的落点(如 grpo.max_num_steps / max_steps)。
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""CLI 单测默认在临时 StarForge 项目内运行(不再依赖 dogfood 仓里的 experiments/)。"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from starforge.cli.commands import common
|
|
7
|
+
from starforge.cli.repo import init_repo
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture(autouse=True)
|
|
11
|
+
def cli_project(tmp_path, monkeypatch):
|
|
12
|
+
root = init_repo(tmp_path / "sf-project", name="cli-tests", git=False)
|
|
13
|
+
(root / "experiments" / "demo").mkdir(exist_ok=True)
|
|
14
|
+
# ROOT 由 __getattr__ 惰性提供;setattr 会先 getattr 触发 project_root()。
|
|
15
|
+
# 必须写进模块字典,后续测试的 monkeypatch.setattr 才能覆盖。
|
|
16
|
+
monkeypatch.setitem(common.__dict__, "ROOT", root)
|
|
17
|
+
monkeypatch.setenv("SF_REPO_ROOT", str(root))
|
|
18
|
+
monkeypatch.chdir(root)
|
|
19
|
+
return root
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""sf bench:模型引用解析与评测提交参数翻译。"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import io
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
from typer.testing import CliRunner
|
|
8
|
+
|
|
9
|
+
from starforge.cli import cli
|
|
10
|
+
from starforge.cli.commands import bench as bench_cmd
|
|
11
|
+
|
|
12
|
+
runner = CliRunner()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_run_ref_resolves_hf_export(monkeypatch):
|
|
16
|
+
class _Resp(io.BytesIO):
|
|
17
|
+
def __enter__(self):
|
|
18
|
+
return self
|
|
19
|
+
|
|
20
|
+
def __exit__(self, *a):
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
def fake_bearer(srv, method, path):
|
|
24
|
+
assert path == "/api/jobs/run-42/artifacts"
|
|
25
|
+
return _Resp(json.dumps({"artifacts": [
|
|
26
|
+
{"kind": "checkpoint", "path": "/out/step_100"},
|
|
27
|
+
{"kind": "hf_export", "path": "/out/hf_export"},
|
|
28
|
+
]}).encode())
|
|
29
|
+
|
|
30
|
+
monkeypatch.setattr(bench_cmd.api_client, "current_server", lambda *a: "http://srv")
|
|
31
|
+
monkeypatch.setattr(bench_cmd.api_client, "_bearer_request", fake_bearer)
|
|
32
|
+
assert bench_cmd._resolve_model_ref("run:run-42") == "/out/hf_export"
|
|
33
|
+
# 非 run: 引用原样返回
|
|
34
|
+
assert bench_cmd._resolve_model_ref("Qwen/Qwen3-0.6B") == "Qwen/Qwen3-0.6B"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_bench_submits_evalkit_spec(monkeypatch, tmp_path):
|
|
38
|
+
(tmp_path / "experiments" / "bench-demo").mkdir(parents=True)
|
|
39
|
+
calls = {}
|
|
40
|
+
|
|
41
|
+
monkeypatch.setattr(bench_cmd.common, "ROOT", tmp_path)
|
|
42
|
+
monkeypatch.setattr(bench_cmd.common, "project_name", lambda: "test")
|
|
43
|
+
monkeypatch.setattr(bench_cmd, "gate", lambda: None)
|
|
44
|
+
monkeypatch.setattr(bench_cmd.common, "resolve_profile", lambda *a, **k: "h200")
|
|
45
|
+
monkeypatch.setattr(
|
|
46
|
+
bench_cmd, "_materialize_profile_or_exit",
|
|
47
|
+
lambda exprs: ("h200", ["eval:h200:1:1"], []),
|
|
48
|
+
)
|
|
49
|
+
monkeypatch.setattr(
|
|
50
|
+
bench_cmd.packing, "git_provenance",
|
|
51
|
+
lambda *a, **k: {"git_commit": "cafe", "git_dirty": False, "config_sha": "x"},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def fake_build_spec(exp_path, **kw):
|
|
55
|
+
calls["method"] = kw["method"]
|
|
56
|
+
calls["sets"] = kw["sets"]
|
|
57
|
+
calls["model"] = kw["model"]
|
|
58
|
+
calls["validate"] = kw["validate"]
|
|
59
|
+
return {"spec": True}
|
|
60
|
+
|
|
61
|
+
monkeypatch.setattr(bench_cmd, "_build_spec_or_exit", fake_build_spec)
|
|
62
|
+
monkeypatch.setattr(
|
|
63
|
+
bench_cmd.api_client, "submit_via_server",
|
|
64
|
+
lambda *a, **k: {"queued": False, "job_id": "ray-1", "run_id": "r1"},
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
res = runner.invoke(cli.app, [
|
|
68
|
+
"bench", "experiments/bench-demo",
|
|
69
|
+
"--model", "Qwen/Qwen3-0.6B",
|
|
70
|
+
"--suites", "gsm8k,mmlu",
|
|
71
|
+
"--runner", "evalscope",
|
|
72
|
+
"--limit", "20",
|
|
73
|
+
])
|
|
74
|
+
assert res.exit_code == 0, res.stdout
|
|
75
|
+
assert calls["method"] == "evalkit/benchmark"
|
|
76
|
+
assert calls["model"] == "Qwen/Qwen3-0.6B"
|
|
77
|
+
assert calls["sets"] == ["runner=evalscope", "suites=gsm8k,mmlu", "limit=20"]
|
|
78
|
+
assert calls["validate"] is False
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from starforge import __version__ as CORE_VERSION
|
|
9
|
+
from starforge.cli.catalog import CatalogCompatibilityError, verify_catalog_compatibility
|
|
10
|
+
from starforge.cli.spec_builder import build_spec
|
|
11
|
+
from starforge.recipes import get_recipe
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _payload(spec, **recipe_overrides):
|
|
15
|
+
recipe = get_recipe(spec.recipe_name)
|
|
16
|
+
item = {
|
|
17
|
+
"name": recipe.id,
|
|
18
|
+
"version": recipe.version,
|
|
19
|
+
"digest": recipe.digest,
|
|
20
|
+
"framework": recipe.framework,
|
|
21
|
+
"adapter": recipe.framework,
|
|
22
|
+
"framework_version": recipe.runtime.default_version,
|
|
23
|
+
"supported_framework_versions": list(recipe.runtime.supported_versions),
|
|
24
|
+
"runtime": recipe.runtime.to_dict(),
|
|
25
|
+
}
|
|
26
|
+
item.update(recipe_overrides)
|
|
27
|
+
canonical = [{"name": item["name"], "version": item["version"], "digest": item["digest"]}]
|
|
28
|
+
digest = hashlib.sha256(
|
|
29
|
+
json.dumps(canonical, sort_keys=True, separators=(",", ":")).encode()
|
|
30
|
+
).hexdigest()
|
|
31
|
+
return {
|
|
32
|
+
"apiVersion": "forge/recipe-catalog/v2",
|
|
33
|
+
"contract": {"versions": ["forge/v2"]},
|
|
34
|
+
"core": {"version": "0.1.0", "requirement": ">=0.1,<1"},
|
|
35
|
+
"catalog_digest": f"sha256:{digest}",
|
|
36
|
+
"recipes": [item],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _spec():
|
|
41
|
+
return build_spec("experiments/demo", recipe="nemo-rl/grpo", pools=["all:h100:1:1"])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_exact_catalog_handshake_accepts_matching_recipe():
|
|
45
|
+
verify_catalog_compatibility(_spec(), _payload(_spec()))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pytest.mark.parametrize(
|
|
49
|
+
"field,value",
|
|
50
|
+
[("version", "0.0.0"), ("digest", "sha256:wrong"), ("framework", "verl"), ("adapter", "verl")],
|
|
51
|
+
)
|
|
52
|
+
def test_catalog_handshake_rejects_recipe_drift(field, value):
|
|
53
|
+
spec = _spec()
|
|
54
|
+
with pytest.raises(CatalogCompatibilityError):
|
|
55
|
+
verify_catalog_compatibility(spec, _payload(spec, **{field: value}))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_catalog_handshake_rejects_core_drift():
|
|
59
|
+
spec = _spec()
|
|
60
|
+
payload = _payload(spec)
|
|
61
|
+
payload["core"] = {"version": "9.0.0", "requirement": ">=9,<10"}
|
|
62
|
+
with pytest.raises(CatalogCompatibilityError, match="core"):
|
|
63
|
+
verify_catalog_compatibility(spec, payload)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_catalog_handshake_accepts_compatible_core_range():
|
|
67
|
+
spec = _spec()
|
|
68
|
+
payload = _payload(spec)
|
|
69
|
+
payload["core"] = {"version": "0.2.0", "requirement": ">=0.1,<1"}
|
|
70
|
+
verify_catalog_compatibility(spec, payload)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_catalog_handshake_still_accepts_legacy_v1_exact_match():
|
|
74
|
+
spec = _spec()
|
|
75
|
+
payload = _payload(spec)
|
|
76
|
+
payload["apiVersion"] = "forge/recipe-catalog/v1"
|
|
77
|
+
payload["core"] = {"version": CORE_VERSION, "requirement": f"=={CORE_VERSION}"}
|
|
78
|
+
verify_catalog_compatibility(spec, payload)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_catalog_handshake_rejects_tampered_catalog_digest():
|
|
82
|
+
spec = _spec()
|
|
83
|
+
payload = _payload(spec)
|
|
84
|
+
payload["catalog_digest"] = "sha256:tampered"
|
|
85
|
+
with pytest.raises(CatalogCompatibilityError, match="catalog digest"):
|
|
86
|
+
verify_catalog_compatibility(spec, payload)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_catalog_handshake_rejects_framework_version_or_runtime_id_drift():
|
|
90
|
+
spec = _spec()
|
|
91
|
+
payload = _payload(spec, supported_framework_versions=["9.9.9"])
|
|
92
|
+
with pytest.raises(CatalogCompatibilityError, match="未发布"):
|
|
93
|
+
verify_catalog_compatibility(spec, payload)
|
|
94
|
+
|
|
95
|
+
payload = _payload(spec)
|
|
96
|
+
payload["recipes"][0]["runtime"]["versions"]["0.7.0"]["runtime_id"] = "wrong-runtime"
|
|
97
|
+
with pytest.raises(CatalogCompatibilityError, match="runtime_id"):
|
|
98
|
+
verify_catalog_compatibility(spec, payload)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_submit_does_not_package_when_handshake_fails(tmp_path, monkeypatch):
|
|
102
|
+
from starforge.cli import api_client
|
|
103
|
+
|
|
104
|
+
spec = _spec()
|
|
105
|
+
packed = []
|
|
106
|
+
|
|
107
|
+
monkeypatch.setattr(api_client, "current_server", lambda _server=None: "https://lab.example")
|
|
108
|
+
monkeypatch.setattr(
|
|
109
|
+
api_client,
|
|
110
|
+
"verify_server_compatibility",
|
|
111
|
+
lambda *_args, **_kwargs: (_ for _ in ()).throw(CatalogCompatibilityError("drift")),
|
|
112
|
+
)
|
|
113
|
+
monkeypatch.setattr(
|
|
114
|
+
api_client,
|
|
115
|
+
"pack_working_dir",
|
|
116
|
+
lambda *_args, **_kwargs: packed.append(True),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
with pytest.raises(CatalogCompatibilityError, match="drift"):
|
|
120
|
+
api_client.submit_via_server(
|
|
121
|
+
"experiments/demo", "h100", tmp_path, spec=spec
|
|
122
|
+
)
|
|
123
|
+
assert packed == []
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_submit_requires_explicit_profile(tmp_path):
|
|
127
|
+
from starforge.cli import api_client
|
|
128
|
+
|
|
129
|
+
with pytest.raises(ValueError, match="profile"):
|
|
130
|
+
api_client.submit_via_server("experiments/demo", "", tmp_path, spec=_spec())
|