diff --git a/src/soup_cli/recipes/catalog.py b/src/soup_cli/recipes/catalog.py index bf3c143..c9a754f 100644 --- a/src/soup_cli/recipes/catalog.py +++ b/src/soup_cli/recipes/catalog.py @@ -4111,6 +4111,105 @@ training: moe_aux_loss_coeff: 0.01 gradient_checkpointing: true +output: ./output +""", + ), + # v0.71.30 — bundled openenv rollout envs (out-of-the-box GRPO). + "grpo-env-calculator": RecipeMeta( + model="HuggingFaceTB/SmolLM2-135M-Instruct", + task="grpo", + size="135M", + tags=("grpo", "openenv", "rollout", "calculator", "reasoning"), + description="SmolLM2-135M GRPO on the bundled calculator env (openenv rollout)", + yaml_str="""\ +base: HuggingFaceTB/SmolLM2-135M-Instruct +task: grpo + +data: + train: ./data/seed_prompts.jsonl + format: auto + max_length: 512 + +training: + epochs: 1 + lr: 1e-6 + batch_size: 4 + lora: + r: 16 + alpha: 32 + target_modules: auto + grpo_beta: 0.04 + num_generations: 4 + reward_fn: verifiable + verifiable_domain: math + rollout_backend: openenv + rollout_func: soup_cli.envs.calculator:rollout + +output: ./output +""", + ), + "grpo-env-retrieval-qa": RecipeMeta( + model="HuggingFaceTB/SmolLM2-135M-Instruct", + task="grpo", + size="135M", + tags=("grpo", "openenv", "rollout", "retrieval", "qa"), + description="SmolLM2-135M GRPO on the bundled retrieval-QA env (openenv rollout)", + yaml_str="""\ +base: HuggingFaceTB/SmolLM2-135M-Instruct +task: grpo + +data: + train: ./data/seed_prompts.jsonl + format: auto + max_length: 512 + +training: + epochs: 1 + lr: 1e-6 + batch_size: 4 + lora: + r: 16 + alpha: 32 + target_modules: auto + grpo_beta: 0.04 + num_generations: 4 + reward_fn: accuracy + rollout_backend: openenv + rollout_func: soup_cli.envs.retrieval_qa:rollout + +output: ./output +""", + ), + "grpo-env-guess-number": RecipeMeta( + model="HuggingFaceTB/SmolLM2-135M-Instruct", + task="grpo", + size="135M", + tags=("grpo", "openenv", "rollout", "deduction", "game"), + description="SmolLM2-135M GRPO on the bundled number-deduction env (openenv rollout)", + yaml_str="""\ +base: HuggingFaceTB/SmolLM2-135M-Instruct +task: grpo + +data: + train: ./data/seed_prompts.jsonl + format: auto + max_length: 512 + +training: + epochs: 1 + lr: 1e-6 + batch_size: 4 + lora: + r: 16 + alpha: 32 + target_modules: auto + grpo_beta: 0.04 + num_generations: 4 + reward_fn: verifiable + verifiable_domain: math + rollout_backend: openenv + rollout_func: soup_cli.envs.guess_number:rollout + output: ./output """, ), diff --git a/tests/test_recipes.py b/tests/test_recipes.py index 04f5304..b36ef45 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -260,7 +260,7 @@ class TestV025NewRecipes: assert cfg.base == recipe.model assert cfg.task == recipe.task - def test_catalog_size_is_134(self): + def test_catalog_size_is_137(self): """Total catalog size — grew with each release. v0.25.0 shipped 43 recipes (29 + 9 Part A + 2 Part B tools + 3 Part E MLX). @@ -272,10 +272,11 @@ class TestV025NewRecipes: v0.62.0 added 3 (raft-llama3-8b, ra-dit-retriever, ra-dit-llama3-8b) -> 116. v0.71.24 added 17 (2026 model-family expansion) -> 133. v0.71.25 added 1 (qwen2.5-coder-7b-sft) -> 134. + v0.71.30 added 3 (grpo-env-calculator/retrieval-qa/guess-number) -> 137. """ from soup_cli.recipes.catalog import RECIPES - assert len(RECIPES) == 134 + assert len(RECIPES) == 137 def test_new_recipes_searchable(self): """Search returns the new recipes via keyword/task filter.""" diff --git a/tests/test_v07130.py b/tests/test_v07130.py index 9c22f35..d581db9 100644 --- a/tests/test_v07130.py +++ b/tests/test_v07130.py @@ -456,6 +456,38 @@ class TestGrpoPrmWiring: assert captured["spec"] == "accuracy" +# --------------------------------------------------------------------------- +# Task 6 — recipes +# --------------------------------------------------------------------------- +_NEW_RECIPES = ["grpo-env-calculator", "grpo-env-retrieval-qa", "grpo-env-guess-number"] + + +class TestRecipes: + @pytest.mark.parametrize("name", _NEW_RECIPES) + def test_recipe_resolves(self, name): + from soup_cli.recipes.catalog import get_recipe + + recipe = get_recipe(name) + assert recipe is not None + assert recipe.task == "grpo" + + @pytest.mark.parametrize("name", _NEW_RECIPES) + def test_recipe_yaml_parses(self, name): + from soup_cli.config.loader import load_config_from_string + from soup_cli.recipes.catalog import get_recipe + + recipe = get_recipe(name) + cfg = load_config_from_string(recipe.yaml_str) + assert cfg.task == "grpo" + assert cfg.training.rollout_backend == "openenv" + assert cfg.training.rollout_func.startswith("soup_cli.envs.") + + def test_catalog_size_is_137(self): + from soup_cli.recipes.catalog import RECIPES + + assert len(RECIPES) == 137 + + class TestNoTopLevelTorch: def test_prm_reward_has_no_top_level_torch(self): import soup_cli.utils.prm_reward as mod