From 1d7fae4a63f5e3a30e1927559d28a348a2167dd6 Mon Sep 17 00:00:00 2001 From: weijie Date: Fri, 18 Sep 2026 21:24:14 +0800 Subject: [PATCH] feat(cod): release CoD v2 Add CoD workflows, credit assignment, environments, and mixed-domain on-policy distillation. Include release training and evaluation configs, data generators, and bilingual documentation. --- .gitignore | 9 + .../data_prepare/message_splitter.py | 15 +- examples/research_cod/README.md | 148 +- examples/research_cod/README_zh.md | 151 +- examples/research_cod/assets/cod_qwen27b.png | Bin 0 -> 1043808 bytes .../bench/eval_alchemy_easy.yaml | 89 ++ .../bench/eval_alchemy_hard.yaml | 89 ++ .../bench/eval_frozenlake_obscure_hard.yaml | 73 + ...val_mixed_opd_steps_0to60_all_domains.yaml | 156 ++ .../eval_source_checkpoints_all_domains.yaml | 168 ++ .../eval_strong_api_model_all_domains.yaml | 176 +++ .../exp_plan_final/bench/eval_terminal.yaml | 68 + .../exp_plan_final/bench/run_eval.sh | 62 + .../train/alchemy_abl_no_adaptive_red.yaml | 134 ++ .../train/alchemy_abl_no_adaptive_red_is.yaml | 135 ++ .../train/frozen_lake_obscure.yaml | 120 ++ .../exp_plan_final/train/grid_navigation.yaml | 122 ++ .../train/grid_navigation_27b.yaml | 128 ++ .../train/mixed_flobs_alchran.yaml | 155 ++ .../train/optimal_control_improve.yaml | 130 ++ .../train/pde_control_grid_opd.yaml | 235 +++ .../pde_discovery_cod_600steps_hard.yaml | 180 +++ .../data_prepare/1_info_extract_pipeline.py | 288 ++++ .../data_prepare/2_build_dataset.py | 85 ++ .../data_prepare/llm_info_extraction.py | 199 +++ .../data_prepare/make_ood_split.py | 139 ++ examples/research_cod/get_alchemy_data.py | 59 + examples/research_cod/get_frozen_lake_data.py | 85 ++ .../research_cod/get_grid_navigation_data.py | 68 + .../research_cod/get_optimal_control_data.py | 231 +++ .../research_cod/get_pde_discovery_data.py | 513 +++++++ examples/research_cod/get_terminal_data.py | 118 ++ pyproject.toml | 5 +- tests/common/pde_discovery_test.py | 574 +++++++ trinity/algorithm/advantage_fn/__init__.py | 1 + .../algorithm/advantage_fn/cod_advantage.py | 461 ++++++ .../advantage_fn/multi_step_grpo_advantage.py | 13 +- trinity/algorithm/algorithm.py | 2 +- .../policy_loss_fn/rec_policy_loss.py | 34 +- trinity/buffer/operators/__init__.py | 2 + trinity/buffer/operators/cod_operator.py | 14 + trinity/common/config.py | 18 +- trinity/common/experience.py | 4 + trinity/common/models/model.py | 3 + trinity/common/models/vllm_model.py | 15 +- trinity/common/workflows/__init__.py | 10 + .../connect_the_dots/agentscope_utils.py | 224 +++ .../connect_the_dots/alchemy/__init__.py | 5 + .../connect_the_dots/alchemy/common.py | 57 + .../workflows/connect_the_dots/alchemy/env.py | 159 ++ .../alchemy/prompts/__init__.py | 43 + .../alchemy/prompts/system.jinja2 | 61 + .../alchemy/prompts/user.jinja2 | 22 + .../connect_the_dots/alchemy/random_graph.py | 432 ++++++ .../alchemy/workflow_random.py | 313 ++++ .../connect_the_dots/base_workflow.py | 304 ++++ .../workflows/connect_the_dots/cod_utils.py | 190 +++ .../connect_the_dots/cod_workflow.py | 909 +++++++++++ .../connect_the_dots/frozen_lake/__init__.py | 30 + .../frozen_lake/prompts/__init__.py | 84 + .../frozen_lake/prompts/system.jinja2 | 28 + .../frozen_lake/prompts/system_obscure.jinja2 | 36 + .../frozen_lake/prompts/user.jinja2 | 11 + .../connect_the_dots/frozen_lake/workflow.py | 371 +++++ .../frozen_lake/workflow_obscure.py | 222 +++ .../frozen_lake/workflow_obscure_react.py | 150 ++ .../grid_navigation/__init__.py | 17 + .../connect_the_dots/grid_navigation/env.py | 267 ++++ .../grid_navigation/prompts/__init__.py | 54 + .../grid_navigation/prompts/system.jinja2 | 35 + .../grid_navigation/prompts/user.jinja2 | 14 + .../grid_navigation/workflow.py | 218 +++ .../connect_the_dots/learn2ask/__init__.py | 0 .../learn2ask/prompts/__init__.py | 67 + .../learn2ask/prompts/reward_judge.jinja2 | 29 + .../learn2ask/prompts/system.jinja2 | 11 + .../connect_the_dots/learn2ask/workflow.py | 369 +++++ .../optimalcontrol/__init__.py | 22 + .../connect_the_dots/optimalcontrol/env.py | 516 +++++++ .../optimalcontrol/prompts/__init__.py | 82 + .../optimalcontrol/prompts/system.jinja2 | 102 ++ .../optimalcontrol/prompts/user.jinja2 | 5 + .../optimalcontrol/workflow.py | 673 ++++++++ .../pde_discovery/__init__.py | 8 + .../pde_discovery/candidate.py | 197 +++ .../pde_discovery/ground_truth.py | 286 ++++ .../ground_truth_families/physical_full.json | 61 + .../physical_full_eval.json | 21 + .../physical_full_eval_4000.json | 31 + .../physical_full_eval_hard.json | 24 + .../physical_full_train.json | 46 + .../physical_general.json | 38 + .../ground_truth_families/physical_hard.json | 13 + .../physical_simplified.json | 23 + .../pde_discovery/pde_numeric.py | 293 ++++ .../pde_discovery/prompts/__init__.py | 28 + .../pde_discovery/prompts/system.jinja2 | 127 ++ .../pde_discovery/prompts/user.jinja2 | 53 + .../pde_discovery/regression.py | 417 +++++ .../pde_discovery/update_context_workflow.py | 46 + .../pde_discovery/workflow.py | 1360 +++++++++++++++++ .../connect_the_dots/terminal/__init__.py | 2 + .../connect_the_dots/terminal/commands.py | 1101 +++++++++++++ .../connect_the_dots/terminal/env.py | 636 ++++++++ .../connect_the_dots/terminal/goal_check.py | 77 + .../terminal/prompts/__init__.py | 41 + .../terminal/prompts/system.jinja2 | 29 + .../terminal/prompts/user.jinja2 | 7 + .../connect_the_dots/terminal/task_gen.py | 996 ++++++++++++ .../connect_the_dots/terminal/workflow.py | 209 +++ .../update_context_workflow.py | 208 +++ .../workflows/connect_the_dots/utils.py | 85 ++ trinity/common/workflows/workflow.py | 52 + trinity/explorer/explorer.py | 39 +- trinity/explorer/workflow_runner.py | 24 +- trinity/trainer/verl/losses.py | 26 +- trinity/trainer/verl/trainer.py | 3 + trinity/trainer/verl/workers.py | 12 +- trinity/trainer/verl_legacy/monkey_patch.py | 6 +- trinity/trainer/verl_legacy/verl_config.py | 1 + 120 files changed, 18179 insertions(+), 63 deletions(-) create mode 100644 examples/research_cod/assets/cod_qwen27b.png create mode 100644 examples/research_cod/exp_plan_final/bench/eval_alchemy_easy.yaml create mode 100644 examples/research_cod/exp_plan_final/bench/eval_alchemy_hard.yaml create mode 100644 examples/research_cod/exp_plan_final/bench/eval_frozenlake_obscure_hard.yaml create mode 100644 examples/research_cod/exp_plan_final/bench/eval_mixed_opd_steps_0to60_all_domains.yaml create mode 100644 examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml create mode 100644 examples/research_cod/exp_plan_final/bench/eval_strong_api_model_all_domains.yaml create mode 100644 examples/research_cod/exp_plan_final/bench/eval_terminal.yaml create mode 100644 examples/research_cod/exp_plan_final/bench/run_eval.sh create mode 100644 examples/research_cod/exp_plan_final/train/alchemy_abl_no_adaptive_red.yaml create mode 100644 examples/research_cod/exp_plan_final/train/alchemy_abl_no_adaptive_red_is.yaml create mode 100644 examples/research_cod/exp_plan_final/train/frozen_lake_obscure.yaml create mode 100644 examples/research_cod/exp_plan_final/train/grid_navigation.yaml create mode 100644 examples/research_cod/exp_plan_final/train/grid_navigation_27b.yaml create mode 100644 examples/research_cod/exp_plan_final/train/mixed_flobs_alchran.yaml create mode 100644 examples/research_cod/exp_plan_final/train/optimal_control_improve.yaml create mode 100644 examples/research_cod/exp_plan_final/train/pde_control_grid_opd.yaml create mode 100644 examples/research_cod/exp_plan_final/train/pde_discovery_cod_600steps_hard.yaml create mode 100644 examples/research_cod/exp_plan_learn2ask/data_prepare/1_info_extract_pipeline.py create mode 100644 examples/research_cod/exp_plan_learn2ask/data_prepare/2_build_dataset.py create mode 100644 examples/research_cod/exp_plan_learn2ask/data_prepare/llm_info_extraction.py create mode 100644 examples/research_cod/exp_plan_learn2ask/data_prepare/make_ood_split.py create mode 100644 examples/research_cod/get_alchemy_data.py create mode 100644 examples/research_cod/get_frozen_lake_data.py create mode 100644 examples/research_cod/get_grid_navigation_data.py create mode 100644 examples/research_cod/get_optimal_control_data.py create mode 100644 examples/research_cod/get_pde_discovery_data.py create mode 100644 examples/research_cod/get_terminal_data.py create mode 100644 tests/common/pde_discovery_test.py create mode 100644 trinity/algorithm/advantage_fn/cod_advantage.py create mode 100644 trinity/buffer/operators/cod_operator.py create mode 100644 trinity/common/workflows/connect_the_dots/agentscope_utils.py create mode 100644 trinity/common/workflows/connect_the_dots/alchemy/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/alchemy/common.py create mode 100644 trinity/common/workflows/connect_the_dots/alchemy/env.py create mode 100644 trinity/common/workflows/connect_the_dots/alchemy/prompts/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/alchemy/prompts/system.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/alchemy/prompts/user.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/alchemy/random_graph.py create mode 100644 trinity/common/workflows/connect_the_dots/alchemy/workflow_random.py create mode 100644 trinity/common/workflows/connect_the_dots/base_workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/cod_utils.py create mode 100644 trinity/common/workflows/connect_the_dots/cod_workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/frozen_lake/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/frozen_lake/prompts/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/frozen_lake/prompts/system.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/frozen_lake/prompts/system_obscure.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/frozen_lake/prompts/user.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/frozen_lake/workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/frozen_lake/workflow_obscure.py create mode 100644 trinity/common/workflows/connect_the_dots/frozen_lake/workflow_obscure_react.py create mode 100644 trinity/common/workflows/connect_the_dots/grid_navigation/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/grid_navigation/env.py create mode 100644 trinity/common/workflows/connect_the_dots/grid_navigation/prompts/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/grid_navigation/prompts/system.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/grid_navigation/prompts/user.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/grid_navigation/workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/learn2ask/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/learn2ask/prompts/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/learn2ask/prompts/reward_judge.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/learn2ask/prompts/system.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/learn2ask/workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/optimalcontrol/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/optimalcontrol/env.py create mode 100644 trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/system.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/user.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/optimalcontrol/workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/candidate.py create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth.py create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full.json create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval.json create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval_4000.json create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval_hard.json create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_train.json create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_general.json create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_hard.json create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_simplified.json create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/pde_numeric.py create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/prompts/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/prompts/system.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/prompts/user.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/regression.py create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/update_context_workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/pde_discovery/workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/terminal/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/terminal/commands.py create mode 100644 trinity/common/workflows/connect_the_dots/terminal/env.py create mode 100644 trinity/common/workflows/connect_the_dots/terminal/goal_check.py create mode 100644 trinity/common/workflows/connect_the_dots/terminal/prompts/__init__.py create mode 100644 trinity/common/workflows/connect_the_dots/terminal/prompts/system.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/terminal/prompts/user.jinja2 create mode 100644 trinity/common/workflows/connect_the_dots/terminal/task_gen.py create mode 100644 trinity/common/workflows/connect_the_dots/terminal/workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/update_context_workflow.py create mode 100644 trinity/common/workflows/connect_the_dots/utils.py diff --git a/.gitignore b/.gitignore index 27aec6eebfb..70e40523f52 100644 --- a/.gitignore +++ b/.gitignore @@ -101,3 +101,12 @@ wandb/ # checkpoints checkpoints/ + +# local experiment artifacts +.codex_snapshots/ +tmp_vllm_stub/ +examples/research_cod/data/ +examples/data/optimal_control/ +examples/research_cod/pde_discovery/results/ + +playground.ipynb diff --git a/examples/learn_to_ask/data_prepare/message_splitter.py b/examples/learn_to_ask/data_prepare/message_splitter.py index 06362b05b3b..b28017b7625 100644 --- a/examples/learn_to_ask/data_prepare/message_splitter.py +++ b/examples/learn_to_ask/data_prepare/message_splitter.py @@ -10,27 +10,22 @@ def split_single_message_list(messages): Returns: list: List of rounds, where each round contains messages and remaining chat + + Note: consecutive same-role messages are preserved as-is (RealMedConv is IM-style). """ rounds = [] round_number = 1 i = 0 while i < len(messages): - # Collect messages for this round - round_messages = [] - - # Add messages until we reach a user message + # Advance i past one [non-user]* [user]* block (one "turn") while i < len(messages) and messages[i].get("role") != "user": - round_messages.append(messages[i]) i += 1 - - # Add user message(s) - if there are consecutive user messages, - # include all of them in this round while i < len(messages) and messages[i].get("role") == "user": - round_messages.append(messages[i]) i += 1 - # The remaining messages (if any) form the remaining_chat + # Use full prefix as round messages (paper Sec 3.1: C_{t-1} = (u_0, ..., u_{t-1})) + round_messages = messages[:i] remaining_messages = messages[i:] round_entry = {"round_number": round_number, "messages": round_messages} diff --git a/examples/research_cod/README.md b/examples/research_cod/README.md index 2f4219a4eea..b94c4a70dcc 100644 --- a/examples/research_cod/README.md +++ b/examples/research_cod/README.md @@ -10,8 +10,6 @@ CoD groups related tasks into a pack and generates a long rollout trajectory int The whole pack is trained end-to-end with RL, with fine-grained credit assignment rewarding context updates that make future tasks easier. A trained model's reward rises across pack positions, which is the signature of the elicited CoD meta-capability. -> **The full implementation lives on the [`research/cod`](https://github.com/agentscope-ai/Trinity-RFT/tree/research/cod/examples/research_cod) branch.** Check out that branch to view or run the full code. -

CoD overview
Figure 1: a visualization of CoD-Deploy and CoD-Train (compared with standard task-by-task RL). Environments A and B are used for training; M is a new environment for deployment or evaluation. Each block is one rollout episode, for solving a task x_i (which may itself be a long-horizon multi-turn task) or for updating the agent's context z_i about the current environment. @@ -40,7 +38,12 @@ Key metric: `reward_iterative_hint_e2e_taskset_{ts}_pos_{pos}`, the mean reward

reward rising across pack positions -
Figure 2: the CoD effect. Reward rises across pack positions during training and at OOD evaluation, both in-domain (harder FrozenLake) and cross-domain (Alchemy, Terminal). +
Figure 2: the CoD effect with Qwen3-8B. Reward rises across pack positions during training and at OOD evaluation, both in-domain (harder FrozenLake) and cross-domain (Alchemy, Terminal). +

+ +

+ CoD-trained Qwen3.6-27B compared with Qwen3.8 models across three environments +
CoD-trained Qwen3.6-27B compared with the base model, Qwen3.8-27B, and Qwen3.8-Max on PDE Discovery, Optimal Control, and Grid Navigation.

--- @@ -55,16 +58,41 @@ In each environment, the tasks in a pack share something reusable: sometimes a h | Alchemy-Random | `cod_random_alchemy_workflow` | A hidden crafting recipe: which elements combine into which new element | | Terminal | `cod_terminal_workflow` | How commands and paths work and their pitfalls, and roughly where files live | | Learn2Ask | `cod_learn2ask_workflow` | When to keep asking vs. when to stop and give a diagnosis | +| Optimal Control | `cod_optimalcontrol_workflow` | Learn the system's hidden dynamics from feedback and use them to reach new target states | +| PDE Discovery | `trinity.common.workflows.connect_the_dots.pde_discovery.workflow.CoDPDEDiscoveryWorkflow` | Discover the unknown reaction term from sampled data and refine it across tasks | +| Grid Navigation | `cod_grid_navigation_workflow` | Explore a shared cost map and use accumulated observations to choose lower-cost routes | + +Implementations live in [`trinity/common/workflows/connect_the_dots/`](../../trinity/common/workflows/connect_the_dots/). -The workflow implementations live under [`trinity/common/workflows/connect_the_dots/`](https://github.com/agentscope-ai/Trinity-RFT/tree/research/cod/trinity/common/workflows/connect_the_dots) on the `research/cod` branch. +--- + +## Layout + +``` +examples/research_cod/ +├── get_*_data.py # environment data generators +├── exp_plan_final/ # main study +│ ├── train/ # training configs +│ └── bench/ # eval configs +└── exp_plan_learn2ask/ # data prep for learn2ask + +trinity/common/workflows/connect_the_dots/ # CoD workflow implementation +├── cod_workflow.py # packing / iterative hint / task reward +├── base_workflow.py # AsyncCoDMultiStepWorkflow base class +└── / # each environment has its own subdir + ├── workflow.py # task rendering / scoring + └── prompts/ # system / user prompts + +trinity/algorithm/advantage_fn/cod_advantage.py # reward-to-go credit assignment (CoDAdvantageFn) +``` --- -## Run it +## Quickstart ```bash -git clone -b research/cod https://github.com/agentscope-ai/Trinity-RFT.git -cd Trinity-RFT +git clone +cd conda create -n trinity python=3.12 && conda activate trinity pip install -e ".[vllm,flash_attn]" pip install gymnasium jinja2 pandas @@ -78,20 +106,63 @@ python examples/research_cod/get_frozen_lake_data.py --local_dir examples/resear python examples/research_cod/get_frozen_lake_data.py --local_dir examples/research_cod/data/frozen_lake_6767 \ --train_size 50000 --test_size 4000 --map_min_size 6 --map_max_size 7 --tile_min_prob 0.6 --tile_max_prob 0.7 # Alchemy-Random -python examples/research_cod/get_alchemy_data.py --local_dir examples/research_cod/data/alchemy_random --train_size 50000 --test_size 4000 --seed 42 +python examples/research_cod/get_alchemy_data.py --local_dir examples/research_cod/data/alchemy_random --train_size 50000 --test_size 4000 --seed 42 # Terminal python examples/research_cod/get_terminal_data.py --local_dir examples/research_cod/data/terminal --train_size 50000 --test_size 4000 --seed 42 --composite_ratio 0.5 +# PDE Discovery +python examples/research_cod/get_pde_discovery_data.py \ + --local_dir examples/research_cod/data/pde_discovery_runtime_seed \ + --train_size 50000 --test_size 32 --seed 42 +# Optimal Control +python examples/research_cod/get_optimal_control_data.py \ + --local_dir examples/research_cod/data/optimal_control \ + --train_size 50000 --test_size 4000 --difficulty hard --train_seed 42 --test_seed 2024 +# Grid Navigation +python examples/research_cod/get_grid_navigation_data.py \ + --local_dir examples/research_cod/data/grid_navigation \ + --train_size 50000 --test_size 4000 --seed 42 ``` -**2. Train.** +**2. Train CoD models.** +Set `TRINITY_MODEL_PATH` to the local model directory and adjust `cluster` (node count / GPUs per node) in the YAML for your hardware. In the W&B project selected by the config, watch `rollout/reward_iterative_hint_e2e_taskset_0_pos_{pos}/mean` to compare rewards across pack positions. ```bash # FrozenLake-Obscure trinity run --config examples/research_cod/exp_plan_final/train/frozen_lake_obscure.yaml # Mixed (joint training on FrozenLake-Obscure + Alchemy-Random) trinity run --config examples/research_cod/exp_plan_final/train/mixed_flobs_alchran.yaml + +# Qwen3.6-27B +pip install -e ".[qwen3_5]" +export TRINITY_MODEL_PATH=/path/to/Qwen3.6-27B + +# PDE Discovery +trinity run --config examples/research_cod/exp_plan_final/train/pde_discovery_cod_600steps_hard.yaml +# Optimal Control +trinity run --config examples/research_cod/exp_plan_final/train/optimal_control_improve.yaml +# Grid Navigation +trinity run --config examples/research_cod/exp_plan_final/train/grid_navigation_27b.yaml ``` -**3. Evaluate.** (per-checkpoint OOD generalization: harder in-domain + unseen cross-domain) +Mixed OPD uses one teacher per domain, with tokenizers compatible with the student: + +```bash +# Convert teacher checkpoints to Hugging Face format +PDE_CKPT=/path/to/pde-run/global_step_75 +CONTROL_CKPT=/path/to/control-run/global_step_200 +GRID_CKPT=/path/to/grid-run/global_step_100 +for ckpt in "$PDE_CKPT" "$CONTROL_CKPT" "$GRID_CKPT"; do + trinity convert --checkpoint-dir "$ckpt" --base-model-dir "$TRINITY_MODEL_PATH" +done + +# Mixed OPD: PDE + Optimal Control + Grid Navigation +export TRINITY_PDE_TEACHER_MODEL_PATH="$PDE_CKPT/actor/huggingface" +export TRINITY_OPTIMAL_CONTROL_TEACHER_MODEL_PATH="$CONTROL_CKPT/actor/huggingface" +export TRINITY_GRID_NAVIGATION_TEACHER_MODEL_PATH="$GRID_CKPT/actor/huggingface" +trinity run --config examples/research_cod/exp_plan_final/train/pde_control_grid_opd.yaml +``` + +**3. Evaluate.** +Evaluate each saved checkpoint, measuring out-of-distribution generalization both in-domain (harder versions of the training environments) and cross-domain (environments unseen in training). ```bash # FrozenLake-Obscure ckpt → FrozenLake-hard (in-domain) + Alchemy-easy / Terminal (cross-domain) bash examples/research_cod/exp_plan_final/bench/run_eval.sh --train-tasks frozen_lake_obscure @@ -99,6 +170,63 @@ bash examples/research_cod/exp_plan_final/bench/run_eval.sh --train-tasks frozen bash examples/research_cod/exp_plan_final/bench/run_eval.sh --train-tasks mixed_flobs_alchran ``` +PDE Discovery, Optimal Control, and Grid Navigation share a checkpoint benchmark. Generate the PDE evaluation set; reuse the Control and Grid test sets above: + +```bash +python examples/research_cod/get_pde_discovery_data.py \ + --local_dir examples/research_cod/data/pde_discovery_eval_hard_stratified_4000_disjoint_testonly \ + --train_size 1 --test_size 4000 --seed 20260902 \ + --eval_pack_size 8 --eval_template_count 25 \ + --eval_ground_truth_family physical_full_eval_4000.json --test_only +``` + +Set `EVAL_PROJECT`, `EVAL_GROUP`, and `EVAL_NAME` to the checkpoint run. `TRINITY_MODEL_PATH` points to the base model; each `global_step_*/actor/` must contain `model.safetensors`. + +```bash +export TRINITY_CHECKPOINT_ROOT_DIR=/path/to/checkpoints + +# PDE RL checkpoints → all three environments +EVAL_PROJECT=trinity-cod EVAL_GROUP=pde_discovery EVAL_NAME="your-pde-run" EVAL_TRAIN_DOMAIN=pde \ + trinity run --config examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml +# Optimal Control RL checkpoints → all three environments +EVAL_PROJECT=trinity-cod EVAL_GROUP=optimal_control EVAL_NAME="your-control-run" EVAL_TRAIN_DOMAIN=control \ + trinity run --config examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml +# Grid Navigation RL checkpoints → all three environments +EVAL_PROJECT=trinity-cod-final EVAL_GROUP=grid_navigation EVAL_NAME="your-grid-run" EVAL_TRAIN_DOMAIN=grid \ + trinity run --config examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml +``` + +Mixed OPD checkpoint evaluation (select a directory containing steps 5, 10, …, 60; step 0 evaluates the base model): + +```bash +EVAL_PROJECT=trinity-cod EVAL_GROUP=mixed_multi_teacher_opd EVAL_NAME="your-mixed-opd-eval-run" \ + trinity run --config examples/research_cod/exp_plan_final/bench/eval_mixed_opd_steps_0to60_all_domains.yaml +``` + +--- + +## Key config knobs (`cod.cod_workflow_args`) + +| Field | Meaning | +|---|---| +| `activated_cod_methods` | CoD methods to enable; the main study uses `["iterative_hint_e2e"]` | +| `hint_penalty_coef` / `length_penalty_coef` | Length penalty on hints / on a correct solution | +| `task_pack_size` / `eval_task_pack_size` | Pack size for training / evaluation | + +--- + +## Adding a CoD environment + +1. Subclass the CoD base workflow in `trinity/common/workflows/connect_the_dots//workflow.py` (see `frozen_lake/workflow_obscure.py` for a compact example). +2. Register `"cod__workflow": "...workflow.CoDWorkflow"` in the `default_mapping` dict of [`trinity/common/workflows/__init__.py`](../../trinity/common/workflows/__init__.py). +3. Add a generator + config under `examples/research_cod/`, setting `default_workflow_type: 'cod__workflow'`. + +After subclassing `AsyncCoDMultiStepWorkflow` (`base_workflow.py`), implement: + +- `step_async(step_num)`: build the prompt for the task, call the model, apply its action / grade its answer, write the reward into `self.final_reward`, and return `(continue, experiences)`. +- `_get_feedback()`: return the environment's feedback for this step (written into `exp.info["feedback"]`, used to generate the hint). +- `max_step_num`: the max number of steps per task. + --- ## Citation diff --git a/examples/research_cod/README_zh.md b/examples/research_cod/README_zh.md index 5743ba76fe7..0d122fb730d 100644 --- a/examples/research_cod/README_zh.md +++ b/examples/research_cod/README_zh.md @@ -10,8 +10,6 @@ CoD 把相关任务打成一个 pack(任务包),作为一条交替进行** 整个 pack 用 RL 端到端训练,细粒度的信用分配让“使后续任务更好解的 context 更新”获得奖励。 训练好的模型 reward 随 pack 位置递增,这正是 CoD 元能力被激发出来的标志。 -> **完整实现在 [`research/cod`](https://github.com/agentscope-ai/Trinity-RFT/tree/research/cod/examples/research_cod) 分支**,请切到该分支查看或运行完整代码。 -

CoD 方法总览
图 1:CoD-Deploy 与 CoD-Train 的示意(与标准的逐任务 RL 对比)。环境 A、B 用于训练,M 是部署或评测的新环境。每个 block 是一个 rollout 回合:求解任务 x_i(其本身可能是长程多轮任务),或更新 agent 对当前环境的 context z_i。 @@ -40,7 +38,12 @@ pack = [ task0, task1, task2, task3 ] # 同一 taskset 的一包相关任

reward 随 pack 位置递增 -
图 2:CoD 效应。reward 随 pack 位置上升,训练时如此,OOD 评测时也如此,包括 in-domain(更难的 FrozenLake)与 cross-domain(Alchemy、Terminal)。 +
图 2:Qwen3-8B 的 CoD 实验结果。reward 随 pack 位置上升,训练时如此,OOD 评测时也如此,包括 in-domain(更难的 FrozenLake)与 cross-domain(Alchemy、Terminal)。 +

+ +

+ CoD 训练后的 Qwen3.6-27B 与 Qwen3.8 系列在三个环境上的对比 +
CoD 训练后的 Qwen3.6-27B 与基础模型、Qwen3.8-27B、Qwen3.8-Max 在 PDE Discovery、Optimal Control 和 Grid Navigation 上的对比。

--- @@ -55,22 +58,47 @@ pack = [ task0, task1, task2, task3 ] # 同一 taskset 的一包相关任 | Alchemy-Random | `cod_random_alchemy_workflow` | 隐藏的合成配方,哪些元素能组合出哪个新元素 | | Terminal | `cod_terminal_workflow` | 命令、路径的用法与易踩的坑,以及文件大致在哪 | | Learn2Ask | `cod_learn2ask_workflow` | 何时继续追问、何时停下来给诊断 | +| Optimal Control | `cod_optimalcontrol_workflow` | 从交互反馈中了解系统的运动规律,更好地控制它到达不同目标 | +| PDE Discovery | `trinity.common.workflows.connect_the_dots.pde_discovery.workflow.CoDPDEDiscoveryWorkflow` | 从采样数据中发现未知的反应项,在后续任务中不断完善判断 | +| Grid Navigation | `cod_grid_navigation_workflow` | 逐步探索并记住地图中的移动代价,为后续任务选择低代价路线 | + +实现位于 [`trinity/common/workflows/connect_the_dots/`](../../trinity/common/workflows/connect_the_dots/)。 -各环境的 workflow 实现位于 `research/cod` 分支的 [`trinity/common/workflows/connect_the_dots/`](https://github.com/agentscope-ai/Trinity-RFT/tree/research/cod/trinity/common/workflows/connect_the_dots)。 +--- + +## 目录结构 + +``` +examples/research_cod/ +├── get_*_data.py # 各环境的数据生成器 +├── exp_plan_final/ # 主实验 +│ ├── train/ # 训练配置 +│ └── bench/ # 评测配置 +└── exp_plan_learn2ask/ # learn2ask的数据准备 + +trinity/common/workflows/connect_the_dots/ # CoD workflow 实现 +├── cod_workflow.py # 打包 / 迭代 hint / 任务奖励 +├── base_workflow.py # AsyncCoDMultiStepWorkflow 基类 +└── / # 每个环境各自有独立子目录 + ├── workflow.py # 任务渲染 / 评分 + └── prompts/ # system / user prompt + +trinity/algorithm/advantage_fn/cod_advantage.py # reward-to-go 信用分配(CoDAdvantageFn) +``` --- -## 运行 +## 快速开始 ```bash -git clone -b research/cod https://github.com/agentscope-ai/Trinity-RFT.git -cd Trinity-RFT +git clone +cd conda create -n trinity python=3.12 && conda activate trinity pip install -e ".[vllm,flash_attn]" pip install gymnasium jinja2 pandas ``` -**1. 生成数据** +**1. 生成数据。** ```bash # FrozenLake-Obscure python examples/research_cod/get_frozen_lake_data.py --local_dir examples/research_cod/data/frozen_lake_4567 \ @@ -78,20 +106,63 @@ python examples/research_cod/get_frozen_lake_data.py --local_dir examples/resear python examples/research_cod/get_frozen_lake_data.py --local_dir examples/research_cod/data/frozen_lake_6767 \ --train_size 50000 --test_size 4000 --map_min_size 6 --map_max_size 7 --tile_min_prob 0.6 --tile_max_prob 0.7 # Alchemy-Random -python examples/research_cod/get_alchemy_data.py --local_dir examples/research_cod/data/alchemy_random --train_size 50000 --test_size 4000 --seed 42 +python examples/research_cod/get_alchemy_data.py --local_dir examples/research_cod/data/alchemy_random --train_size 50000 --test_size 4000 --seed 42 # Terminal python examples/research_cod/get_terminal_data.py --local_dir examples/research_cod/data/terminal --train_size 50000 --test_size 4000 --seed 42 --composite_ratio 0.5 +# PDE Discovery +python examples/research_cod/get_pde_discovery_data.py \ + --local_dir examples/research_cod/data/pde_discovery_runtime_seed \ + --train_size 50000 --test_size 32 --seed 42 +# Optimal Control +python examples/research_cod/get_optimal_control_data.py \ + --local_dir examples/research_cod/data/optimal_control \ + --train_size 50000 --test_size 4000 --difficulty hard --train_seed 42 --test_seed 2024 +# Grid Navigation +python examples/research_cod/get_grid_navigation_data.py \ + --local_dir examples/research_cod/data/grid_navigation \ + --train_size 50000 --test_size 4000 --seed 42 ``` -**2. 训练** +**2. CoD 模型训练。** +将 `TRINITY_MODEL_PATH` 设为本地模型目录,并按实际硬件调整 YAML 中的 `cluster`(节点数 / 每节点 GPU 数)。在配置指定的 W&B 项目中,查看 `rollout/reward_iterative_hint_e2e_taskset_0_pos_{pos}/mean`,比较不同 pack 位置的平均奖励。 ```bash # FrozenLake-Obscure trinity run --config examples/research_cod/exp_plan_final/train/frozen_lake_obscure.yaml # Mixed(FrozenLake-Obscure + Alchemy-Random 联合训练) trinity run --config examples/research_cod/exp_plan_final/train/mixed_flobs_alchran.yaml + +# Qwen3.6-27B +pip install -e ".[qwen3_5]" +export TRINITY_MODEL_PATH=/path/to/Qwen3.6-27B + +# PDE Discovery +trinity run --config examples/research_cod/exp_plan_final/train/pde_discovery_cod_600steps_hard.yaml +# Optimal Control +trinity run --config examples/research_cod/exp_plan_final/train/optimal_control_improve.yaml +# Grid Navigation +trinity run --config examples/research_cod/exp_plan_final/train/grid_navigation_27b.yaml +``` + +Mixed OPD 每个环境使用一个教师模型,tokenizer 须与学生兼容: + +```bash +# 将教师 checkpoint 转换为 Hugging Face 格式 +PDE_CKPT=/path/to/pde-run/global_step_75 +CONTROL_CKPT=/path/to/control-run/global_step_200 +GRID_CKPT=/path/to/grid-run/global_step_100 +for ckpt in "$PDE_CKPT" "$CONTROL_CKPT" "$GRID_CKPT"; do + trinity convert --checkpoint-dir "$ckpt" --base-model-dir "$TRINITY_MODEL_PATH" +done + +# Mixed OPD:PDE + Optimal Control + Grid Navigation +export TRINITY_PDE_TEACHER_MODEL_PATH="$PDE_CKPT/actor/huggingface" +export TRINITY_OPTIMAL_CONTROL_TEACHER_MODEL_PATH="$CONTROL_CKPT/actor/huggingface" +export TRINITY_GRID_NAVIGATION_TEACHER_MODEL_PATH="$GRID_CKPT/actor/huggingface" +trinity run --config examples/research_cod/exp_plan_final/train/pde_control_grid_opd.yaml ``` -**3. 评测**(逐 checkpoint 衡量 OOD 泛化:同域更难版本 + 跨域未见环境) +**3. 评测。** +把训好的 checkpoint 逐个评测,衡量分布外(OOD)泛化:既包括 in-domain(训练环境的更难版本),也包括 cross-domain(训练中未见过的环境)。 ```bash # FrozenLake-Obscure ckpt → 同域 FrozenLake-hard,跨域 Alchemy-easy / Terminal bash examples/research_cod/exp_plan_final/bench/run_eval.sh --train-tasks frozen_lake_obscure @@ -99,10 +170,66 @@ bash examples/research_cod/exp_plan_final/bench/run_eval.sh --train-tasks frozen bash examples/research_cod/exp_plan_final/bench/run_eval.sh --train-tasks mixed_flobs_alchran ``` +PDE Discovery、Optimal Control 和 Grid Navigation 共用 checkpoint 评测配置。生成 PDE 评测集,Control 和 Grid 复用前面的 test split: + +```bash +python examples/research_cod/get_pde_discovery_data.py \ + --local_dir examples/research_cod/data/pde_discovery_eval_hard_stratified_4000_disjoint_testonly \ + --train_size 1 --test_size 4000 --seed 20260902 \ + --eval_pack_size 8 --eval_template_count 25 \ + --eval_ground_truth_family physical_full_eval_4000.json --test_only +``` + +`EVAL_PROJECT`、`EVAL_GROUP`、`EVAL_NAME` 对应实际训练目录。`TRINITY_MODEL_PATH` 指向基础模型,各 `global_step_*/actor/` 中须有 `model.safetensors`。 + +```bash +export TRINITY_CHECKPOINT_ROOT_DIR=/path/to/checkpoints + +# PDE RL checkpoint → 三个环境 +EVAL_PROJECT=trinity-cod EVAL_GROUP=pde_discovery EVAL_NAME="your-pde-run" EVAL_TRAIN_DOMAIN=pde \ + trinity run --config examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml +# Optimal Control RL checkpoint → 三个环境 +EVAL_PROJECT=trinity-cod EVAL_GROUP=optimal_control EVAL_NAME="your-control-run" EVAL_TRAIN_DOMAIN=control \ + trinity run --config examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml +# Grid Navigation RL checkpoint → 三个环境 +EVAL_PROJECT=trinity-cod-final EVAL_GROUP=grid_navigation EVAL_NAME="your-grid-run" EVAL_TRAIN_DOMAIN=grid \ + trinity run --config examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml +``` + +Mixed OPD checkpoint 评测(目录中保留第 5、10、…、60 步,第 0 步评测基础模型): + +```bash +EVAL_PROJECT=trinity-cod EVAL_GROUP=mixed_multi_teacher_opd EVAL_NAME="your-mixed-opd-eval-run" \ + trinity run --config examples/research_cod/exp_plan_final/bench/eval_mixed_opd_steps_0to60_all_domains.yaml +``` + --- -## 引用 +## 关键配置项(`cod.cod_workflow_args`) + +| 字段 | 含义 | +|---|---| +| `activated_cod_methods` | 启用的 CoD 方法;主实验用 `["iterative_hint_e2e"]` | +| `hint_penalty_coef` / `length_penalty_coef` | 对 hint / 对正确解答的长度惩罚 | +| `task_pack_size` / `eval_task_pack_size` | 训练 / 评测时的 pack 大小 | +--- + +## 新增 CoD 环境 + +1. 在 `trinity/common/workflows/connect_the_dots//workflow.py` 继承 CoD 基类 workflow(可参考 `frozen_lake/workflow_obscure.py` 这个精简示例)。 +2. 在 [`trinity/common/workflows/__init__.py`](../../trinity/common/workflows/__init__.py) 的 `default_mapping` 里登记 `"cod__workflow": "...workflow.CoDWorkflow"`。 +3. 在 `examples/research_cod/` 下添加生成器 + 配置,并设 `default_workflow_type: 'cod__workflow'`。 + +继承 `AsyncCoDMultiStepWorkflow`(`base_workflow.py`)后,实现函数: + +- `step_async(step_num)`:为任务准备 prompt、调用模型推理、执行其动作 / 评判答案,将 reward 写入 `self.final_reward`,返回 `(是否继续, experiences)`。 +- `_get_feedback()`:返回环境对这步的反馈文本(写进 `exp.info["feedback"]`,用于生成 hint)。 +- `max_step_num`:单个任务的最大步数。 + +--- + +## 引用 ```bibtex @article{chen2026connect, title={Connect the Dots: Training LLMs for Long-Lifecycle Agents with Cross-Domain Generalization Via Reinforcement Learning}, diff --git a/examples/research_cod/assets/cod_qwen27b.png b/examples/research_cod/assets/cod_qwen27b.png new file mode 100644 index 0000000000000000000000000000000000000000..9126e752a4ed61854af9223d60bbefbf1da6dcd7 GIT binary patch literal 1043808 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

uNlKUz{JHIrk)>5rV0D&G?KKZaR1Ug zP0&oCdwJ$Q8d)KAcc7UYEH_$EUgckRUYhX`P)(?6kUI&b2;Aa_-c|z?6uFi>3YBoo zO&|DUruM=r3Af`2MJw_QV3?2k{7e~R^Q8JY{uV&fp-(}W(||wc`OR?DVPF?AaEEUI z#*71eNnAYl;PV_;g(*C3k^_#SP4ZY|%6O5>d50F8a;eua#=PKz*x$Q7Ba;yTis=Pv2g?TCf26q%x8y>by^%GE1 zNU07d-VR0sIYn~WcX_ROztV^Ai)MTa+=j*%O$*^PQivb4aOyMJVk_mq`cJwX7JusH z!4-jXdh9N7BXUG$cECW1jg{$ZNnwJ2FOX0-2@`xDnG?}NQrb|RdI4rUzwkWu(OQB`*;XTTf*o1`Qy|xb_^2g)^ymAA3I+Ix!;A#IqSaGl zr(*cTG;rR}`RjL!@`f5vT_V5BJK~f9Iu_)cv7Y=uBESRd1zF*>KrXNb*+)ysH6}- zZM-B?;PEMDde?b|KI(VD7=XoLYtkDna7lfaA-wSICH~k975nf5OqBTNqS2M>F(hwp zAsiA9*K+nkXPRDq+3sWeQPJ@IG((9(mCxX;H*tDJS*?cyh`bz}972g71f$bE8>Yr& zJ<08U5kBebmzS0*ylzV)EE_h#L!yVBS=^gtbEcY<+?Q2MgO#Q8BS|29SK6^f@eZLV zx+<>5zEe}gd%Iu59miFdy$NNmfCbjpL%0GPszvpgg2Yu;`Bi*wZSbetOZLCz!_{w1 zOzq3a_i||M5RF0(ulI%^wt>1YV@jfF84iRdp29~Vya(1MwVNqqQBo0p!K`{X+7Fv_ zaGb7-bi=SPt?_K_yvyUYcRLAOTzng=O_Ua2{ycqqNOzkH6AM|5>e3(I>m7R|OQ0QM z5)yXwtM~dLmgC?3J`Gv9yo((K%3J2%b>LEjOVh@IGlpK4OMK_^mP`tbzGvo$wyeia zA)vriw=q6(Hd2kmKe#H>cZ>(+_ zf`OLH$6@Q035S|FL?!oZu`5II6H;T_PRht~d5>+peqVIjwG7Q%@l^~(_;4Ye?hiRB z*uVmhx|;Qm$5~!@7FOTVfv2BmQzfTr)tWbU&ey?lKeO34?xS}T(7WlTZnr=q#209k zwj-90w{uh_+?Jx^wE5S5M@$$E`J|uN0?PKX)1L2la0lF4_ex{~uG$aGj!hcu$e_|K zx7#_c@ncUgXHQDYZA@^5EPI%WjjaVgSRUMdpcwz)GN{F=yhkdY{R?~i#$M=>&#{w= zkul4P)Dvc@i&G%wgE={hME6mg9?U1PKY1Ovu&@u+xJY=9cFBX-_Mc#pIH$&a&nl=6 zD@m`86As#_vJ{1ll<1gPr+J24AloFY^QmYTZ)zK5lB#y3Cf^xfDnUj;xm1@r5d>2Q z$=WL1!Y0zYP-ds!H%0%_tT6zr3>``G(CN;#uMcxF*Yh?RgBAxmv2Ep*J}auh`7%GX zO=Z!)&(yrTk@6OJ3XZl6;oWgZPE0))7W}K*U{E3owD1Y_{C)Y@z{0c{QqHo2I!>4TK> zjBn~esdYGjoqZBmDZ`=z{|Qm&h)LGT$V>@}4ycFT7nGj)1H=`6TGNiQu(Y$rrk7L% zh%;)t@zU`5U_nxbJQ_Wg@nOtVsJ(mZ&ewvY;01tHRw?=V&E>&ZjWwS0Ix-&SN;%9y zj_c_=v~4eka5tZ;pBIp^nApmwLGH$LR~e`55QuP!eTh}~H9UMdC?Nchu*|e#?pd`F z2v~nrW-3m0xs#>yWyuVg+KsRA39^j|pjX2i3kLD%=X08u`1U`q&dz7eJ^o~q+5adR zEmz~p)k!EA<1)1^YUS;Nk5x>y0G?Z{4?{d28IO4u1zldR%)>AjQFo=>inNJ4k<~lL zL_@O+Oy9%ZC|}_#yR%JH0G=5Gp3(hJT{L~2NIjQCH$^?=dRIPQ^r-iQ6AsVz9k4KkPNuZ~t2{ncpC(-5jufA=DLOQ1V1TK8VE|@9o+A0R1vRigwHknk zPw5>aSD=fuMi-Fug5$GNqpO1A~EP-h5yEuugQas zJHaxUOuiTCV$56jP-k)uq$xX(K0m%S(jBWXF@W6wL@YQT+x(R*J4F7kHhz}!zAa$h zd0_4!pGPKe;Cbay@VPeGJeI3B<8Q$k`t|~KKosIyA;62JiBZ3-;hgB^A(M&UGyOsab2>{4gSSTNHDk4rbT`EGl>Ug-}))lALK&XCdEmp|#I z9LW%wg;`??5UZPpa{?O{dq+uQCZ%AwK+Jvd1}&)q%_mA5D-%%_xEZ=^_5wzA1^u57 z%^i1_A-H`kqXyN^wF4_2n%4O^^;Cf?0Rj;)cwJ&$R$C=LF;G!OcTo;JVO%H8n18yy ze)J;yu3;CyUzV&(8Wy#CZTVa$J@5d>65DNI?7af>_Y9uOy}X|cP4r8C4uYGw>K+z+y&(DB0SO4Y%Mx$tCAOX*~=G>35>a1}H z(Bk9U6YU7w!2U`kvBwRM<_aGRKyh3Y%|KSdeK+d^tFkb6x|`Pu3|(Pg!3pQox@*X6 zE`L3NKssnDRJs^r*EZN`@o>NP4(DEq7;FW{)U)q8Hs+26iH7Dp2YP_ocBZIav}AUD zh*?+d#gJ@Pj}h-M4xRO(b3sOivHly+B1{#6?r=W!{)~G=Cl`!6s>;OU&f7L%gd}I7 zow}Nhj&Owuw$lJ1QEH(snr3(GUKl9YL#LqGZ9YEpWl;aHG1#F<+|orbAssCIXCdw` z+kDf0_5|eM$KQY-(IBljDkLoMVBZv9iytXMNgb__*{YW?id_=4?+ZR>Gr!g;4n zy?gOT1QYC?S1efEM+suu{E`BiR+iTJvf0e)-YQ)1!Zt=mouvQ|xzBZx%-e+c&%TDQ&+?BAgN6a;k(r9o*)5 ztY+l)MJ;snn{L1o_dbd^)rUY^JxM6uEuZr?I0}qhhY0{fR-#AQOO+HRHS_8^5TYe9 zLR!4&RLOKfgSeU_bp4E`}1vl+vt=bR%@ByWtwoWt}@}qEkP3@#2Qwx_NxDj??SI`@sW;3FEe5Ketlz6+xN!pE+lKXE+#Pr}TOpMWr}n(gz_BNfuDe4~?%jJ; zJP8=50@{{WW?vD6Nq0;Hy{Kt1lvte4+b4v|2@6IIW9Td%I1`8e*#zIfO|M;OfpAv3 zLv)3rlN~|X9tzl{hAdk1e|dl-+M8@m8j?V;!$UL@23d4(QNW%~T`UEx{PWob+L)qJ zrh82x*$_nwJ8m>@`afQF=+w|mwIx4Nn;-M1wr}IY)Vpc@;aoSdSq@2EHPZ7_aCJ-p zV)4K!&XbHvtGc($NQ#$J;|Bi6;h`@u3Jd+G@f#;{0s&rPp>_b{;`T6ZgpNY!V||tMMhPH1$t-y zz$D+yW4t}xwHU)7$lP(5sKm~-ZKq!C6#j&V6#!FNiG}iBVL?{4I2*jW7|vH&<-wSB z)pv!Rb*%+z>=yaWUb__c@qyxVw^*Xy8XVg86~$^1_vqki);Yv!Iy1 z*yK|skIMP9B)IuyuwqHk>^(x0HJ+sudqHBrJ+&c6CyhJTy zzs=e}aPLdZj945VK;6p5Ob8ci!sXYJ6+$kBQi)ftOvLgKN2gP!p_?$_uzkzhJt4yD zu+)oyLmcuT$P39v^QYZ)lVxvq0+&!9?tg1>F!my7uWWmQ4?L_DsgQW=kQFg1_fd!$*LoLS<#Q^ebmsM6oI#Z zWbin8?PPlK<3oGc5Rp~dLnk@$ECoo`p2Hp9mjIBRtE#4_UU4>ktzN5k|E4m7 zs^`}iOWqg??z)L_3Pvm0o2}$ zh5v;B@-n)f$x-RB06t7Y^<(HS>kojb~>kw_IOF5{L=FGb(u3DIoPCC5Ff9?ErC z_LiYRi-;XAjrgJSS)Pr41+t^@E zM_W(<#z1Lm!+m3%ZTIZDL>~1c#zn|`8fxcmN@QMWr*@$#3Woi*_XyHP`#?QreI`&& zL2eoTXC`AVMEo4;B7>L;B9cnIuZ2$Km$~ruG*h}N@p>mdbdTkZoe7J)phV<`(C2xJ zEFq4qNTea@v@lcF3DAW5X@!y0tYRa>^8BTP48}20y>JqU*hSG~$T7b6liw{#1qQDK za5$V{Z|el((BL%bj^lVEM`-vHs!ECojazNN_V3FN&WY`r}U->-EC8*Xc;xTkqY zEHgk1ZT0p&O+`cRX?G6Z&CCz(){?#J@uX=Hy*)a-u2AwTFrGU%+y0v54e4GP)F?rS!w8CijkYU)}QN29aUPP&wo=G1G9{!nD8#o+?QNfosL3X$gg5Z(<>rl zZ6XlwV||Y`+=@qOQja6JBpBa9t13Nm(T#5e()>yUcy7Nt%^1=o^j z8{rN%?s@pJ(=E+I8gzBa#)>Yv7`Q)o&YjN`x4e1)EAG3;S8e7qPr6?t4;kBHz1&Vw zYubo&!sO;hrBLtw2Pa%_$9MsRGgRuKSJ8!Wh2Jzywi%CSxxF9ZV!kK+A8T&9m0E4s zHI)qS;3#Ps?dHc(tiybmP0na$;{O12IPebHMEU=2eCjbtR^vLq6r-|M+O+#=#OQ?7 zR#%$oJ!MH%!OT zKfvX1kN2_HGo_&I#I3!+We;eR-b`0F+cmrq2$VILW&$?fT&B;a=d%1>6wu;*!*wkY z3}FfmYMmOAxx1b9`q?x(Z|L^g5}n;|irzOK-eUkk`^V?#F!f7efZ@3bZJ zw&QjjEAO$stzo)x1}av_9D9~bvd_gq!9l!Ed_*Ti-anjP%hO<-8e@5ya!mF)vjWMb z7iT79GuAbEZ+6A^SrF!p?TA*hH)W!k`4!M>^JmFslK@dCRlspgo@cWpjtVOE4Ga`9 zSF?|HHewmGcw#_0)$YG(rQ;S`9a+_t7&r#eb8JyO>Rh}D0!|07NeI0SSrkIU!XN7x zPpQ${zns=m&`%-$ec4;H$iC!%Sd$-(39>5Ilja_q)xMni#aLe5<~6nb9)bm~BN{K> z9q_ruC%B?d7-v`NPQngz5n#JL|WY~`uL`K0rdu6ovcg)yt_r*jNbw+^c_d?HmsCbOfb^|tAF!ZxdyOgAunzGw)ono;%g zG?`@aHmy*kjLw^#jbSs^eqo?fgb=qk*mF0X&?qqDi|wfYu>Ff=%iQy&CeEHUcAw=#YCPb|VWM|7Z2RoL zaoa5Qrq>I2%tzg(CB5mR=>OI(b}}Zm_=CqFftT?&_tThtRIwd=ea87uk)ql6cn*b5 zWrzVqRXj-PM(SnfJ0|c)UL?JFga4!kofWpvuA!Pb%!)<3yZ5cSb+}{iII%zTc6lD7 zf{gEn*ket*$Mv@WdDR3b^|5KEl+NS$C-PL{ZyWko9sygt?U`j!X_s|rNd%K1_m2bc zL+$YE$HM=$!SJT@2L|@(`ZpX4^n#d89@`fwPCn-FhzG9mu?_9JMX~X8Q?ll8?tgI} zHYGnTr`w6s3>8=zP3%YMyV-CVU(GVKUPZz{KAVd)3OBJAi_IMu?Uc{W>Q+BqET0v> zY;yurZsSuMtuH!ID|K}kJNU5%OTWN__;$>2jOJhLdCLVp8Ui`Zc9y9)5V1Kn=lGae z)xLI>F5M18yn1iB%^;0)DvouB29pSlJFj)m(*Tf$GtIIiJ)5@AriGBlNhjZo@AFh#KBU}``k(xTDgx!gYq%g#gVsxte*ULi)n5(V5;SRMG`gRMn&i7(hzMruJQ7m! zVkQ*(+fIVEZCE;V#_m>fF~4+q94}%?YQZ`r@I!-8jL%HFH(MF+V%e990VonWq2dS! zZaMN_XbD#Ct>7}zIG!Wu>YzYOr-d_d&p`EFS2laqSFPqdCRlCMETMEl{kO3`8|*52 zV5WXvYGc}ZU44~+T3ywl>irXTN6G1dc*r=!3s>fVxt1u=Us+)Y-N>MGy6G|8V+xI` z_NZVx)gXqUJ_+FN3cMPvL=f34u#=7#LhczXLIs{_kr_^d)}6u!p83;##tkU2>-;HV zZOYISGgV@|J7Ge`;vnof4l{au$3$#<@%+!WNdD5#>>D za@Y@sv+dh6i{}s=UcMnjIW1B3>j9eWagogN%o%5!F9CfZ-ZR4UCzlX0i_-j zX%}7PbFAHJu`&y!k|Acp5X(rrkAA2G+F|b?8jEFtT9B5(A-%^F&k7KV$&wuQmsWD% zsbn{1ZCiq_`0B-*RcFgjoPjBo3(MIkjRz4&C5F$I7#xD z$dygU+2ItLm)MZAx6uzmf_BkeoNOEHQGdEDJs16EvZaEjUb|l5%2uo4O=aSs#WE{p z`pM26mFjtA$m63(oC*JD+!z5x%vtEYP8OgOP%(GINcpjB794lmvH;D-HLqEC#eNYg zTA^Z)E4kx2BT&@1t3+kvDgol&GB&idJo$;K?h~%HW0w2IbB@)$RRCEj;ZJl|;u;*% zIi5hDB7xsG4zW#1F{7V5?9wE2WGHW7HM^ue_(w6rFNpS-^(o+;XyHHCBUD$e@0D7< z*b351H?IccC?3|!OUS5SQgqggf43@p`OFw!?>__L8C1U^!K-gVD+YFo9A#9y*H3pj z9`M-u@ht$D6L+ZCTgpnFKpF%{b>*6AK$xR|4iuZBtsB6-L!4?OPHO_bfAqxW9Uy{G z+lRosYi21AYX36%u9@% z4!XiQkxhV_SsM6(4*vgrW)T+vVSh>tcM|5Cr3^v$3vXp3Z0_rlezC30MOn^WL@Ueh z)P9QSZ@&`cn7OX}2O$*~))%CjySDHvjo+h(GbgXE{)3>0vnK2=!iS4eL~vVqOEz#^ zQ~MK$!K9g4AB)o1J?xvpO{R71-;RxUv0sgp55gW1zmQx`*}ey`HP-w2wA|+#I@Pt| zv0ERwMw;#Rj7qEkRyM|*=rO^ari~8{QTj?H?*`FErRIi{eg|9-W+og_Ai-jCNzWs6 znU=v@>f1LNlI*7|R?SbK-l^w*4OC2YJrpeOY^gR}?a-Kb3YfPv2@pxDW)Km0Eby8@ z(q&5=-~=_T_3=~$6L%|bZ1WjQW<0ww>*JkwU0g;{I7cWTlB$vb;+Ud{5ppqiB8~S+ zgMHUX<9LPPiQj2b+eQ@g`6vUF6v6Cs<s#MI=pX+}IHqaqOw%MavZgKNz;KjFwhw_DmNb;1SXl5SePX zG0qc61J;%jar0?+F_`toWp2DWPkjM*6MrHdhx#ZTeR0~obbl2hxK7z&q0lhH@UIG6 ztBhWDsBk4wopfnN;jX6*?)sy%#lGH`Ohd^MORY3xTSltNahiz~G9#5iX82@AmgNd# z;slvBJ670~mi-xX*t!UPypm;IfPlRchG*)*qS0%$I93^PB^TrFx^)qAN$`CCi>kP1 zCRK56D|?;QpgEtv#$3nkAN4GWe{heB4RP+j*3lqLR*N-W?=T=VH?URS06rBQj0-k_ z!5QGg{a^mq3t+ZG^fd{4J@Gs8m4LdCH2$;-!>(2#F()X&;}WywG>eo2@Y>S&F}B*H z`G+|@72*1D&s=57`sj>&i=aOG_#K>G4b{edv@eO)dLrwk%`ZNXjK~ zom#fwd%GY*7tL>}?*p`rjk{Ch4V1v<#GKzWFx;w^H z4Z5ig^w4+&T{XhO!Z*|nLhDtBiU1XTN#b&=*Ix@=+SvseO7(u#uUAc3`>%52>e#qb8FN7VtZ_1DB zc`*1q$;!2=vAKV{c3dbA!79K2Nr}XqV7!ZKU2_=y2&;GVf0OK&fFSUi*GwWK@!v5trU*_2GvQB(cM% zD>v6n3r51L$HoL3=yzl?5azV1UmtP}-^W>VsDSnNAN1vHCPq~S+aI+C`v)4nx-zLC zGW%?|<>)hbOqNAqJ5rRv!ym)(|0~JH6(FjSTm$W&`9kfBPdX4W)$k}W&i7D9DS4f0 zcmx1TB4(3(_5vQ7&o4Qah<=rXc!ZhC3-F~(Yd}Uc&@7gX0231;~<{KGyC< zgU(kT3sT#$aI#Y0N~zi2oFbmZ$2AEi#`SUQim*qLrFdHboEXF9v*`+h0>J1MQJx{h z(bIdhm8Ylk^8wE8UPWzOVFtj0HX?0LLH6{3!r8>@0zOe^&qUpLcE&epI91% z88|(7k{h5CW0Z63TRx9lP10c`FxtLPxE*!DM0aR zkS$T++wKtG(;ImqKlw9j<*+~Bl#3{MPj+~Qh9SqKQNrg#C+R7UvoBlr!e)<~Qj_8# z*BJI|oW&OD4Ce;#XMU_O=i*gnrWl#MCJ@Hr-U=AG16nG%l19y@RbTsdwcFMU~NBF=o+wSt{c7V zdF~G58Ah|(rM>Yfb}f-9$SxA?aC{+NdN>nqIrRI7Z6_Ph(%VPj;9O#%oxrDG{F zbBE$8^u}lY31?BBY(a`Z9@xZ*s1Za|&``3l>#T2ocC`gW%C_F7q8JNYvhS z$R8N{e)YwsU5*^iXSudxaTR-p-Ypk5Fi?bLA5C9mrdCMTKY`)UruF46nl{-IwLBkE z;fII79F%=iU60nasKl^84%Zil_*4)&mF#{XRPQd}Yduw@wuH1Usf*AC+=^3S7_j5c z<-eKBfZ>byX49;-T<+aq$k+_0^BI?s;S(5Q)(#b(wO?-?0G06uD-CJ2Y{e6M3an%dN2JvC9YK3JzRWOs!#VYutV=Z;-pQ?Z^-oUYG;}Wa>Ry|Zmj=)xW;$zyET$VWWE7t4&T)n z&l?)emvuaA7?G~}z8)dzx$Je7q9%bK6IhZAeS%PIzry)Mo$;O@dipLH|3O<4tvno~ zzTz`pSxLGT2oNEHIiBDN5)~E&?#!l13kJZ;9y~YM-fFkGl=i?LTPNTv4@HpXp7wa0 z6w;<|nNqv#cZEffjPx<0>!Rga_w)U1gnZYt83{%txU#UUn=(e|`OH(VdsPpDwV%c2 zbD9THaBgNk~vB48&F{#n>##!GG_k`&wEJT?Ych?3FPpD zZoEoPtO_~+;6U8=MO$+bFQ>gm$1sKvsaHbM3);BB$!t9q<0|pi0z{eYoRK+ZzF}_L zB8fEBe&lJPj!Je?wl41?G__CkSl9>%orCw@sInZ_B=1L(3n){$OQ+mIu8f9r8M-05) zh?>yIqc8Il-SLy-`Oz?td@w~DY(R|+cVi)EORz>J49mDb3;PmCzV3`bw=8;g1mPqm zR{LSz^*}X zc&-c;RP9qEp_%B6NmbQr3rh_ljt1ssAc?%UGEkZq3(uoP^REY|25ty-zEiLA<0Op| z+nRARAjmL|CvFy%HDZa=s{R}wsp#hV!5zrrFUrnw&sZ^Pw_>t^^7TtWUW)|bIpbRc zzeho5&XG5$M7QtC;VTe$sVkYeFNdBEm?^Q@QknMxvHH~K!=DLv0Aa{~;+}w2Qxb9~ z3n!P};AlK-UX&_mD!@PO33}P?Bnz z#B;-7x3?0r?r z;ZDoffB|zYkTpshvj#A^){$f8%Vzh*=GySHO7^QC(X^&2%{9`A84iO`&~dCD?S#UG zl}^<#- z>%-YQ*}thfC&RAKF#{rOmshiGG~z4@)of-hPG=ze&QHM=ItVn24w=chPM&S;G5OA0 zzFZBmqQ{QkwD~O#OLWQ8+){49 zA33jAq5S!-7jFfNJD6Z?+rzh9MG&@2=B_6DdUum%5}YZh1~HSCML(ai!3h*Aj))$7 z+@EF_s_XsygkLw0ljwhnlLHy0XqSm|^|LONV#o9rb8|Nc{>%F=~Hj*Wog^ z4Js}cr5W@_nO*Z_PcHy#l?S}C1kJXYqk#bcuA`qie4TX7WDkW1m%s}UGym$+nQ5x4 z@|}Bnk8>U#Ok@ak0)DpuwzD=phtbF0yM|l> zjU7KGt9$k&7dlv!^vs0a@s_#aZJ@A5wb0%ptBlD>r9*}?3|iitB4`%BFK3n%QWE>V z*RN$*NT%ZG--!(ScOXwMKJ2}j?v~-Qo!2Mu4_}H)n7v4(sj%(MCet&>aHv$tauf8T zlFVS;m`CejQJxBr{C(yyh$I%RFW?{Uwf!y|n?P^&!L=ODl}@uh^Ju=TWM678`IQ5B z<0CBWmOb6A!u#GN-|GMV4#9~soY+WcG!0znwQXDp{(zy$u->lYe9eU8qj#X1&9@2q zkrF6g>55>S=A=a>Gm+IKfo(e&x|l;cOB~zkBJ^dZtX}JH8oD@EMKUw3OG?n5nT;TY zNIT{U1bl{m8^Ef-A&9%;xbu*fZwoDrn|zV7aHEcA5g=M<}D*SJgU=3nfB-?MR zcqKT$w}*i*c)@Lq&fKFoTO9xKWL6#rG^ffr!U%&DZ_hTq$EyWFB$}D5YacNw-YnX* z?~5mPPD!Ia^MB0q#8NlrwuATsn=lBz5{qpKV*X8sqJr*o-F~b0(Y#L3zFC%_NK+W9 z;d*qTaS1!S4%sUy&#GIwtJjwsTDPae}m5^jN^2b$qvhv>5>DB#EJc8b_@zgMU zTD=wf@4KCsSB_*zh)*b!$9<0nGP-RFS0D;`!EaZO(f9BdD-1rkx|1&iB_Q-mD+b?>r(Tl%o@RmjX?tpvD0(QDNS zr}Jnq??bK|4;j|sK}^4JwXdSZleqb^cNYIm0d512v*d#*0y?ek9lKY^L)e5LL`ObP zT}ly_iNhT%f4>&Nt&7xQ0l~t!sqX2&;h3PS$;5Dg_J&4*@V*GIpN9n@s^sP^0AmF+ zg1~4qhrcsLmGeEW67Y8&_~3X0i?|S7@$i{rAM4A8Z|<|MEL~06eBD{!qnY6ZPd%96 zYskL1I9mH|bG^5m>uG&-&n6=a4Ijd!KDWTG(TIf1w(&c6(Mz!Mj*Ds~AX)T@aasfV zEh-a;Bf#;QQV-pJP#gXRUm6F>jElReftItgLA*iXBBPuCE}(uS9OQ0K<_oR1_~jZ zG0x)3#QBUA1lZb&VgITc~euN*ZNmWUkE+;ncs7EWR}xF(}s zHsbCKokHw4^3F#s%~OQd4cFb|tiC~Ntgi!mg&Nm+_gKZp;GkO@wTj}DV-Z^$yj}|r z33F%%@IDe7dIBacRl{hhFo1!%Wrz{ph3PfHqv{QQP>B{m{QpoTF}%-4$hyAryYFI1 zohTD0FBU2cT=P-TrDv1wr@FKK^v@RMFeY*)Fje;Ats-hTQN3fybk{K(u{H#s=$yZ? z(`iy~8S3+TH4ikutIaOS(BMc;Hxv;caoLvt>#1}NkBPNu^UW`}YSSgC7NpoVY=YY{Gq68Dlq^b7pVve zyp{jS+jr4VwIY(|O0;(IQngI8M><}dIaK%^JNwDUK**Hf%RQc~be7W=0p!L^MJK=8 z=v2{oqLVycOi#w%#9%NX1d>nYo3TYruU^1>KPepvA~LvC741lDJgnu9;!YQ6%YtvM z75@U|K-Fml(csB)k|eao>BKoUZ0&eJy2I=8DE4Fi-y+GS36}|Mcx*zi6g|-i=ASL|Vxu$7#!}!$JPTdd zrQ-NyTOSEWf;*h(+5Gowzf|GeRz^&M!cY2dYUhXS^Hy&zm*@!JU$ZiAUA;J#*W>&~7fQf`ZwxaR`C$&A6!D&u) z*r`ebvTR5*vL*{0>>qRQ8R$wm9?Mr?KLc#rPIS4nx@VtW9&-jn$WX>vY=yc6eKRUm z^xnC)O!xKGW~9vP?w1#aQvtrL+vL&8(5!R@@OE^fFX{NyEwM*@AOGK@og>qcD6!#a z8U5uYFIDI8K}3JHJF^mRRmr#{Wlx0Nf1y9LAWOVI(R^Yw#40&dx~q5ZjokAe%E`@A z@MO0Q|KHb@oV-F&v)Mau9CJI?4CnmP_@v}UA#+dE>qnvg!9Gt_NuBHg_RalK^9)1Mn_PNSF!Hm+$Ou(O zr>-k75G31V3e<=^rVP#@L$A&Yg#qtSY76>98GSm0C0565^h==RAu^>XeL(f2<<2$O z$MqR!n^r-z+5EQJpmQdjki=;tyJ|e_KAwtR^c*0EM^nHXyzl2}i7zct3+#4x!(yPX zjHh@)3D#weEgo_-d;u)F_9Qs(igB0>MUev*TAIex>jDXy!#xB>{=l8Ta*dIA@I6}t z(j5@!L%JHvB1H=6&hHD2!#NirPAC2!CXT}739;|jh*y25Z7<>A5%|TdP`!7Ju`T?; zVBm@IlLpv##G90~VwwbZJVtZZ3fq;D_4m57SHiv%)Oo$1*Q>*LBdJ0h--V=EY4aWU zo9MB4@_0p3yz!DJf1mfHF%FGOkbLu?}kHA`0gP6l19OtBQB*+yEgc6 z_yS_vHJm0GVS*C5xmB9v#pqU=4<18v}eSFA{1beq^d z-HllZ+*Be!=IurdH$IyMwA-l4w{F1r-uH(f5I6>hvc`#b<*N~f zdM`t}pMqI9l#m^W2n)L3zoKY5tCwvqJ0STWiO1tj6joS?f>7fJ4(}^cf$0I_MWJ}i z27L=)$`j`hqkD!n-Ncsa=aH4CxIK>4s!7<2S0ZTsz-)!qnlYS~*8*1sL*{u|y?MC> zP0BdtcZief4GhvFTR%9YoQM(tgH!7N8{vPl4n+9L7c>KNKht-pl5e2msC$8W$Z3AN zgd5CE*+bSjdrfJR?q;5K695{J_DXK!?m^Si=G!_a0Z^Ak5Ckh(tHmdlo6VCvbowtY z-Aqlxq4E$g^cT?OvH6lydPk9ZJzp-MyUOb8$!4lvvzBwmt}1r827MFXw! zywx5|s%nzIA~cC^tp104+Ri~!N2ImvjHW@t{i?W!D?JVd2P*>GW=IpU%)vEL8qn4PmwzX659?@&opyfONC7}63pySq?*g5-V7zyWgV#w= zMT;ld7}-K0ULb0lWkh&2p+`Yn<$_^lmkh(p5|~@=|Gr36Dm1%SW@1d(x@5h_ec zuWT$eZiq2nFLXC{VeR6EkPR9^ap?WpAmi@x-|CXC@Cn3&!`f;IgTI!ByNYVD1zV98 zVZ39BGRj~B?fs_1f|HL*ARJGCwPoNRkyctobKmH_6Q7x#;6i(|d*4Y%%UtV=Zd?`3k5)iQbt*M{ z=Y_8L4d@x5NpyeduuUjKY)-1_UAI~VwtL9gO^`H==AwLI({iVun?I-pObYDcvk!k~ zzPE{3f45k^(`9_a2@9T0SZoGqh2m~D=X+sXr{xv9H?@GDxHCZ}Vf9_3n+4eMfOFs< z!2$N!W^G|vZ?v&#EoPMrok9-I2hG(t8mm|Hs(O<^ZTsA&1J`U@Dva;^Jn1L7MHw$I zW1F`7G;1(i{jj##&u((|SpdQ0h+nRoQlOGkLZo<$ZANfk^4Z(<9a7H8eGZ2p9ftDT zg^KuiP}7*PZpNL;pta z%G09v-t94ngL1)$|u)ST^DPm1d zPZMupi4@LJjt25+tIfTUB+St?MDZ|GZF%K#F3o-8kf48*SgE2F$bkh*YSX)$Dx3|n z5HR81{>)PsNlmTgYA((r9Fdvk*J^#~YyslNpW2IA#WZ&~4sune^y2@JVf0-jzh?+l zeu(_;g>|(7S^!fg9e}Z5#;cSp29tH5=8Sc+4C(i@@lbLEXM5z&lUGuyOFJ0sEm@T6 zaeY)&s&5XXQzvtzh^gv_30ErOgZn{?X+6+bt5$LMt|t)D=9A-=;TVp>Tw88xt{BL- zn~xJCks_L(nudtXu0F=TQdfY4udEE*4~ctXbGaXv;Zerf#9*sDWsmiTa&rA%0q(cUajW7*D3InMxSTI z$L)~3YI81P4PrnU3 zC9@hbj5ca>!H=7E3oUuxJagZ$sV*XCfqA+w$sYEqW!4_za!{*9{`&{c2MhJs0}b}2 zkX9U}E}zYt_4N@w5Vm1zw$?@xHO;~1Sbi#sFEgUSJ(4b@q1A?lolSc!=P|Cx5oT7h*pkfHw_UcSHDuHa`c5^*VxuO zrjq7t`0KkY#=!dG5up_GHwIK2PBi7#o+CeYrn9e4eGm>UZ*S7H6xie zvj}4+lF2_MYs+`<9S%#GY}1hEX6CcUIr`$Otuj5IrZvmPOhLf&!7pko`*-}4zmAqA zkD+%8a^Lv>y*E#W-5On4mI&Etof!mM%!n}gnmPY+WeD1E3 z#HWs}^+Z=UP;!J+h>@KD!%H^T`T_>>ajCBwEX~KmH%~NRtp|tJxGyDYoQ+=Y$dw>u zJ-}V)ZCh#jAq09ehx$@}2>ILcH|S(bLlbe>alow~C0biWjon|ri$a(V_fpE>^RR9c zOcQ2zxBHL}rxIUdLPWwIlwLQt4Kpf7j3u^IauOcM)kf(Vx#q+*I7DyGJl=-<5@AtJJbK{JdO?k<<_t9#!NQmwFN>i4p$Bj1dxr7(3}$LD5Q1P+;o-p z57uLOczngv^H1-Q#8K%ysQA00;KQ1z&Lq1A?MP1H($D5Z$I;59!Arll`|GbsT6oa}=`a5(Zdp#vnXx#3 z4^teo{QDJ!l&6YNq&OQ$WJi7ry*m`07Et?UE))*;OR0v&Cz<&`yXcW{3tYljG@UNuoXEWJE{G|W!{7)F{Sju(ci zUxr-h*f{!(3nI?jJumyim|lWeW2Q9ezzm|5L*xTlbXS{7+t1%5+!f}P8PM+TY|Mip zz%`Gr{l|yHri{=3Vm95zXZPKD4*KLbkvmh8|0`MROx0*32K=h%qM`l~%PL&7;mG6D+)2OZ49qE#;dpfVBII!UZxy)%`uI zn4Q)vJ1(Po-jM|W#;!4>L*)zP`*ob-EG#m9 z_e=$imy{6r-AM^LBH9oful56nT~sko`<)?9P);5V^caf%u6DvXbf{Bg)-7VdH!8~h z^Y$&CEWbJVEIJ~1&^ZG$v+{`@vy*J&X{ed_x4p9OAr5^~w8j1zYhByrX##N#Rxf>i z^+r^QO;e%&i!rMnYXNUDpD1#&&f;B?p)?(@C>=#AYi4z?tZl<|qX{>LX|UD;pFS0Q zug{L7hgeowv7{}RD?8MR1kRq)W65?>Z6Pj!9wHR|EZaFBkZW1MDnHHrf-(BfQfe$O z5djz4w&g`CW|@dkocRX2dX}1oR#1w4nWu5?-IGMq6ilF;XvAiZsBCsAb|hfhVRwl& z_cihQ=n$FcD3jd2y+~ISDsj2r%t=H04ei60viM_{+qPEB|Ie{F^Og`L5n*%9MiTu%3Qk z1-ES3Dp4lX6Btq!XpJB*MC{Gzg<%^De7okS@H!bcWSwQBi!~0f#-vv++7Ofoh30cB z|9LciM{Yd4>m&HjjMLA^Qhqx$*Y9#RBX#;4(B-|7MH7$1Rt#g8uLxq(oxPQupP@`4 zG@_w*YC@Xx{e)3L=&{2D2#-mhh#e9{scaklo%%k7vbXs%f()IXU(b9+=a^plh$QY7 zT63zq+}jxz)@<$6zRn41=Wwo(nf8DC1a?5;eR}YyC(n$a7{yA17RXMuEyR~7;5Xdh z^}uAi!B_(I-*Mv1l|g%E3z1B&rY8KiLMpi5t)aLaT8G9pZ|)+dkX=2jv%{gn{&YOt zW+DwD&LcO(e96ix(xC%T4+v0CHE1@38I@_#*0Hf*qfO^e;dlO{zq=*Sy&q7 z6hD*p1cHA1+Fw%tzR}5a@5s;Zm%~)gY6LkaxrgbrU*2S9J)wVJ3S*}~%8}TNW;%WO zk&f2wCVDB>viL3de$7X?{q@9Dn~w-eg4m6!(=5nlb;|l%N}l}U#JQHxt`5+d`8rxp z;)mZAzD2<7VxX(2bD~02_rm_B(3Zs}Po)0vW0;GUsBLlQP}�B4zF3 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

Ab(ZVKrZG8`tT^FMB?~eyWQ7VnA%^8OLe@YBvGv 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ò@q;>55(Nn4!eQ9rg+_b~+Y2`=i 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 z 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�& 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

2(`gv@_Is1rh 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⪼OsOxx|Cc1;s#^xJz-t&ZHRnSH6-= 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

{qeiYbLwqAUeWnMC>N#3FhYOd+6A;pyR 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�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,DUz-Q+3oZxgD@aC|2j5FojX=&XhVs^zNnkkM7 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�=YeRbUiBtrax2u&3!y~K%|5TKTkS@xOd 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�r)2!;uP&02-$9q@mGZ1j)M)q0D`-V*1Y-&k4x2!EoE=Ou z9G;5J{GJT30g>J{WhMXL!!RS&;Rws5d^Yb~6vuf-;V&jMU!P$`XwHzyUR+3)GtrL~@}U6B0lfl9&ta;9NZu_XzNhrjhmmuF zOwke&dTdNmNQvMWJF7(FIHL@-1Yl%ay&YBt62m5VpJl8n9X zXgmkYseMSyXY-|lM&)FUO`3FyYLT&YrO%D;TzyyYVAiPE~^oHU(nu% zZEelr5Eg=BI3~DqM0|4w?_)P|w3Lgi8u7CT+{xriFO|rmP88=j^+Yg1$wyMDd@8|W zX{MqvW<&Exh>yG^&X~=cOvW zTAV$ATxa8s?*WT4zav{?49IKO{a#Vtzu~Smmh@IGWmY6wnCQXfXcG>ez4|BaOD^1s z0B*0nBsXG4GtlO4uJ6=pN{@yfoS&zYgg#?VAJ{Z z3$|THj#(wQaOe&trU29RFv<6d0IPZS5Jms=`DeEwhy1q|8$B=XhVe3~B z&T=MmrSN!K9PmV!)G(ZVa+0^lFV6|i^;!$p9M)=*Gq@p(UEo)rM$=(?vrC6@XQ+)p z2>~(@5u0{ zp+--`Gkmb<#^jCPY&`5uU=zlGh^%EO*9fH#i55@R3%-4CU4D*IkQKjsb>FBbgSU~u zMXlica~`vIo~+1OqkGz{Up~!e!gJhPtQAMc}&GCO1^E@pUCG zsmF8lz61UzlkX&Xu{R-WrvEYdfFz)aH)qG3UPg{mknC|G-rc_2~q= zs8LwfxJpG_GZWj=l@HOb%DD4SqFZua^Rt^pJ%a2d!NZ;{_{_UzJZlKZ6@YOfMe^Jr9%<6?f)KrO?URjr`8^Uz&!Mu$u=2Ei2-%ujAWR#>gk0hoIb?U z0P`^Ip+zUZo-V2vto^TvNVtDX3UXI@2J?K~<}hF1Pr3-(#toUyXOluM#5kQBG|WES4c5K%)$|C!%-rf5n!B;hE{RNJS8-KO=Uy^2>-t5lgA zP#Ag_R_Y|hRIAqWEi4Qg>EcB#Aiv`Jngd(1IUKDiE|~qOy#xbH}hG$UB4 z&JblIr&81a8j^`O#T=NDCHW}|{i9U!XdiVDLmreqsi(kKU$89&ZUA7a#E+VmktflF z@LXVtLnqnBLe1Z*DbtRZUgR`G(NxR8x@)HpoRkb22zPUC$i2RJ+wIr@q2CX2zi6SJa zz<*=y79dSpY7Lg?>`Soj&E*Dp=A9TcDqd@FSA|gI^}N6*AAFDUZ#TPKgL*kSNpbqY z>1`~GK+jQ&|ISkf{#5N5ML7z2x_W?Mr)eu4p4yc}bahWnBGv!&F!lXdV2dO=qjmIM3~qh|uqc>zPmNp<{P z{mk!05I5)F6uw9rD07{=eRf^IEK6<3bIkkk(h!2*cCJ4C5ZiD)D#Gb?UZo?M%`Yaz zBARg$S#tbtU1hJ8Tc%sSrqLu~;M(jez4(<+tWlkd%SJNs7v<`8)Sp5KF>#`UL6u10 z6=3rgRIAPA2R~}4eX~5fVX43FJMgDYPIcAnaaM%cgXPt*I@yxjnLbZekNA2vPls!* z&gbvv%f6w{d*V0w4k_BOp68_Q0d$F)xI)2lVVI+H%wOy89h}=M@7t)j2e_RbL0n&g z+GYsL9XhM(#*}i>+;FT4e`Jb`b8hKqU5`sq97VACfz+Sq-RY5)>Y>l&8x3e36aOOa z+(^@aoL_hKYUq zCf9K&`r(JDj+~9UbIYM$?VS<^b|R?9M;PiucU}BR5&k2SuKBU;(%BiPv<6-3R$LP5 z9(qRub;-q+W`=}JLna*|D6z~tKxMU&dNmetb0x65hiU~OSQY#RtEcu{YqTntIshIH z951kQ9e3)QwAVR&>A2SxbVFXj4}jp?d~gtk*28Je-mC;H-%J)7X^l*BH;8*KKK8}M zj?5QCRTF-GKDl({+oKWdQ_lF0Z>b%G8K>bm{bzZY(g1YQT_w9OE zSmq}9ZpoE*lH#tH?k4EnGjP$1O@`!c+fPeWV6c!Mp{=8E$`(UV2q_%!?U9&UKokDHD4YGb zf*^pKYc4Mbt8BI2EeTLy_q)X{6I8zdRMp3Sw^+>CYpus;{?y-_nJ*Z1>8m@_+dnVnV5@jrJXSy> zQh_BT5z3Whem1@ZOf|wAaV{*{A(-m?IaSwroVxGL8hS2(Xu$Y)@}0Jk!AgtjDTpp1 z5OV?4v9f@K50^lO)x5-zW3ABa7j;{;p%Qle-)m)hSNAo0@R{#d8KWI6~W}g)Li906(C(QDp4tf3)M7zu7v*$lh&H}35rdyb+ls`bH zmw$2^po6MmkT!KE6(94*Kjx2@0Uek5eXaSP*(WTw@4(c_`(!i~a=Gi?I26;evzUIF z7S%v3Xjl;WYy^@%=|)rFhhHBt(#B_ENVORE!;Zi0;H-DqVcUp*^2(Weg*_j{No#Vn zI)|dN9{*SI#sxY=*ZJ>{xeLc^9?$juc`PG+|3o(Fi8wlT{ShKFD`1V1=Pz z-;>#=e|{y74B!|cr#fk`j^(WJFtJpLjSAVmWUC%*9tm<+_&u5&trqPvjTQxEY;biW zH2ZY<)lX6n`TO|q*E-q!W&3uOcS3n|@OI@H+1%3Ly8|kFbh`z~zYklVd0w`($l{`5ip5Wi4N2Wmh zI#;EWrT$hvNSv+{dTNS!$>qurQWl-OP(rmn3M62x1YRbnRrDmGZE?FNqf4`LfA$;6`Ip)! z)SsubqeWg(+pAG$SN;*W0S-Z!L4a;wgfGjXd*wOyu#McfO0)^IwpfwIphmkD-Z!Bm zptCFieUjl)Qs@$wD9m5K0E8#wqrw<5rUR8{E}|&o<6z9v^CPi6SGNNiF*o~E=xx|h9vX`I15vD6UuLPv})#H(jpkl^3)Ua1s~~`0MZ4@ zn+?iVnlOXlgFExFG^C9iU~R8nZZI^ZAcM)??_4X60R--{62BdJgE(Hx9K`aU;YnV( zXT6pyRjO(^BK^VlsT4|*>3X<5YZ{dfR%gCWqazWigxL>?fZvLX5SXTLiBM|+Vol(S| zj9gWioBLF(L8r;Xeo3%mICF`h-=_XRx(h7YD({vQ|E8F(zEgg*u1srLxn6@NZDnTg z^>ITh%~p9`#ewV%)%^1jc)o_JwN`GgSLnBIx}|Z2L9gI2f`+y=I_%ErZWB34PgkGT zu|7#-vYIy6vh~u@J=26=r=%Z=Gv5oDD9g<*29i z#8W);hpF@Y{Gu2AHYW%FhXAYGj&;tEymBxl2iNhdD6*)S=7uYnvZvrHG+-<7KeqzR zFPH`!7H#_6t~;TSqnEh**?ahumt$CL1AuT^K1$$zrs1ANFh zCcZ_vi=i?tK8>XvPtPqTS+M+E#!JEUDzRJx_PVSh6Vl#KAKx#x=nM()_<=RZ3W*#* zfSrY+YbR{6G(VQpm%73 zFkd^ACwvCsRTcKiY*Zsh^l%zO%N=pt&Z! zqk}A~ifIHxE*HDgp=#%|r3-%#dDdv>nXS1^MJyWxBrnH0l=gzS5fa6)fhez)G#3lA<*jU&V9U%|c7n0Df)% ziov#FQMUtnH|UuQnTdAMIDb&N#xDPezE8xvU<~>o9-#K9odCi&NUMKtqd9j*{K^^E zY+Xmyy}>5MGd<{q#o2pwRoB11*AR5yJ2h$2kWt?K^^~bP(Mn9UxF!X00%bk!(JMIZ z@C(%uf-rKNFPxm5?bcDi`-Hd}I_dA8ZPJi;UTkcHykz(OE1Cd&#FWx-RQ<2O{I9_L zcQa<`8l~ksj-kCMm80s`MZKr!$c}7(x(rs5!eQlr(o2EoLNTyfJgm=E{by_?+cOWn zJXMyB5NE{%x0p*=d}l)*X;NW0{l^!3(gR;-`^2%1%}vk6RzBJyE2DZo{<^XyWEyHF z<@LIXs#i7#TEMfY5LrKT2ITFq5^395qx?eJ@@>@Vos5;+*7<=o)a#tCU?@}eb=}A` z))htQYeRguTUz=mCh3v%n8)GXnfv&?{r&s+>gp1T)4Sgct5RDqdHsJeiB(`ekVI9JT9NiaPybi z(BC*R%})kQv-;1WmqIc15&}Q(u{~RY*$=p?gJO-#OSStLLij;n#KIR8`}+3h4Yaw} zl*ostuZ7#bry0IE3|u%n0uE6JLOnV1#(MRh<>>$tn0w$hr$U2bHl~!dEuPqsA>8@# z7wU3fp+m0z>s*PR!nCg7HfB=+`My|1Kklj3_pWDqKCxZT`WoD~uYa*9z-i=JCe!-FrN%IM0Va z1&rFnN|;J*FjGsGXV|2YecBZ_nJ3G6?4Z;bk`UjJTb^q&F0MyNcofM+G%xMjT{{08 z=deJh`al(dUxySDdj8mat2`wby-;ItD!O$oO&$vH zZ7kT08%nRWZ%YTx7}417c_%({3%}^7E}KdC5S=8fUhCrT4W>yoNeHL{C`V8f7Ck5L z$s~*+MfmM=qLm}b>awwz(R22mn(=wJ0A4~^-PlD%PB;1Q7f5v=Njuww6`#q${f(GXZbqIT#sm`c_Rm8ZWN*pa{%+fYzsU4 zPxu7VMHh_`IlH8nv$fhm<4Q0G#9p>h{?97DhNWv+PagkOs5~%Vq&O~f0ihEh6uRKeQ?Q9Zc@XUCLw0kWKu4`#VSJu{LXLyNHF#I zU3)7tU0GEz4xF*DeWXSA=C$^N%5y0q=>wjTz3_`(Zl-8UT0|_JC}a!6ThVAQ7O#q< zX8)a^5wNnk@1=N~;V3}n=NsRtx!=ov#gFqDQg<`S3wCuXPigiHxxLR~#Rh{OPi`7w z+!+As?Yt)&UwTiBw@pyskmHDNRS`;5b@&mTh(nWj6^-f#xkSXR%!<*Qldvpf zRtj*k8E2kHzIwSx0uSyBdGAbf!`DRajeu#?k3dj{rPny>E*Tb^SeAb5uc#GSq!TmJJrd2;9nqqP#SStdUk3CY+I*yFc{QZrj`8xk`6Jet)L| zF>F$9a-a646;uc!3Blc3X>^{5!`$em48-C^V+NSbWPDy5D&Kszj=M6T-)^0?=F~8_ zh#B>u1k3)1rB6>wqORwY4Y@(e4xSyXMNxazkhp|HJG{!{m;&fAux}x!B4?Y#Te?73 zJ-WyJjFm?9>I667jf6YD*o&fHIl-r$BqqJ`&7#URr%WZ2ItU-+DMy+s;A*^B=IvlJ zb$p=-u;}*5!6FRY#Ig-n{yk)2iAYHZm-Gwl)W5rt`~MR+m^!_7+;2tZhL%5KBW}mF zYxg%}3tbkv4~tY%y8-gxxm=ge?Z(od>(-K=-%{Rx_a%`P3` zBN6u43r0%J`R=@(W72;>D#RzfG+8S`NgXt2X)BBro)S$ZU1y}`50^7V^}h1IM8U%M zHeC)DpTE=9Gi@+GcUnCuPF7TA>xq znVbFWZ1rV=5yn%ebWTC^8yk)E^{bf8J<=@~x}%bjF}VO<8?wqLPc9EzHyY_eD8UHv zLyUv#r>JfI6H#bXcwPF93*+U^9amo(=QS&@x^q8NSA&Ircv7Km^t?~}vuN}||MiM1 z)TAN;_DWQ^XQKJ#_zf>BfYy}EcfCOzR6h=ir5DjY6JtW?w{;~xNB;xY#*uk)H9$VH zH_;A!iqzk$%6+||N@G!&=PNRU^y46<(eQbpeG**611Z2!d8U?cJ3GQQUW)?JaS&R70ru*Pp-@`A*$ zI;Yk}rIXNJ7HcQMadq&Hy+LvP*hVF5a3QdRezQ$6;)0T2>Sa+0nms>u>z76?bc1}y zb)(H1i24bWJZ*%>y4>})2!Sypg{m^e2(@W-R8opVL-*q{wGaZ}vV=s+3;PV?sk z)ep7u_qv$DtV*^;{6;d@_7d+hiD)%)tPdl_d?N9wh>HSZIjLn4Uvu~{+lz7In~BB& z&CH^-iSQ?-mRgNG_8n_JkyX<~|6JSd1EQcQLTPb&Fj_lU=}g>s?MW_p4n^>sWvh3) zmGy!#`eIc44CZTD8MD2pkjs}qfKq_>I4tA5HKzx?tmJKoHvZ~4ARG|NDg$cMuTy5! z&m-gQg)=pt8?`>VYn`p==QYwR{U_MJ0Dq6sI3&#=-6%z$W<4|YMj(YteKR|djPGYJ zyTvv=Sk-nI$_GwyF@idl!>?-`G511*|^U6p3a@Yyr4PL_q;|47hZlvEbh}cK> z1C*~Nlu24hWmuzZM_>bi<2TRtkZ8XJOh&)(0-vEj+MEJksY5W z>{7xA%BAVfP<^)p|Cb>OTmnrlai{bH_zNXAXiF2&WrV={zP@y}ObL8{oXA{tWd5iZ z^ylBPXTwq)#(gX==i~bvf9O4B-eIpD>S70BG-r$|($*{#5JJ-uP{;fbrKjyh1y!0q zbSH|$$Co&k*mWX2#U~1>1|uZgd5osAf%JKOLZb{g>PIhY`7B5Fj4ziS9Xp)*XZ~uw zfCu|MU1xN=*_j(2rOovf=-T-=TjEoKJ&S>YDmyYpX^I5!5TXyYh<-DKyl#CN=?7!3 z_%&B;Hz1jZG)W!`+G7oqpr$;;n|iH?Ug$=j{*pt`II^L38~dVXVU#0_qToEB+vJ6x zI4}t&+FkeAanov3oH)n}CQiSmDp^ndxpe23s@Y4oZXAMu62j#}Zq!ZwRU&f;YQ^`0 zZEU+#Y_a0dW7hq27Q7LD)c$>4;`Ig!t-|jm?)PoLo6%`k_L4F%A4xYTP1=U?Li}I! zG>QbB8&{%OYAI6GAtH!+Ed3!NX*hNZ#=~_9C%)h31v80%tKF-agD6Ou~Ve>K#sbARna7orKM|=Zv$Jcm!4xR=vUkW3zrM^FW>;H5?lu`pBhF)37 zIJ*Epb!&j(X7@|vf<*J^@`))kbtsQL`D`Gb@3@NH&DP^>6zEqb*^9M$+rQ)wB3W~@ zX$C(1>fbHBBR-2}E%=wENyQuiS6bG}J+7lqeg`t1o0G^*oy+zCJ zE6S{~9BfagU|K#qNeOVUjeNQLGQCi|w&NU7^D#@3aVA^1{b%Q&39EO;9Tz9NMHs%1 zL>f++;OPTqE!)gj{_D8I~Ml9saYZ<@jUrA^C6l)n&!evnWNgSDxd& zrv1WaWG+MP-7fZ^11%(!^1W;Xf|(t>>}TSGONdK`9=)*!I!o+e&qy0H^`Z3`kSAG5 zI#Ktmj9bUqQ`fjtG{1uMqvzn8xFTC;ezV%g=2dp3D0G%%JAT~orDqqQwMsH_6p#0dmGfGgRlGp#Coz&tfvr z0&g`R;?jJw)VF@&sm4}w5!O!EJ_8(&*PY9)v3;;%)y1>6N`eYdC$3ICz4b|v`L>K| z_6a;rsRA4py>pMbtxk!)L~jNQp&j@qa1XyBW%S}qYpW|30r!1?PZ6~Wlm9oOqg?xqL5-faHS?;9avX%FXbo63ta#?7 zF=*uay_!3Rhr}IT&s$gY$otmH8kuiX& z**>J@&Gq@8xwZ$_1Y8;cPEa}v@WD1Cg&T(bdZr;)G|N|jJZ+-QkbUL5U$LN%QoU8l z%zAE0M$ys~G@gceQlp*T`2}L0Vq;G4*Klxd8?opUuT*Ls*g%02w5izLFiJjk_1 z9kojG1BgKtHpofAQ<~Ybe}Q;KTei2SP09_|$dj<OJ`sVbkQ_$5~)=ibp> z{)8v}UHBjFOZjgF;bR}8-@jsjrhP>o)&8Q(z$zFqOu7o4k@U2shiI8LF?qFMdeV(A zW;R|r0}^Qd{8^DgU&oaWom6_Dwk)i(^3}%#coq48s=Y}oca$e%Q{GBs`bqmeI7Atv zYB#U7G)Qb@O8vt1v24|iei~(ys^=$FIyk$C|L!Hq*Lg(WCp!+ z;q8z7Ob4GdNS9Aij*M}k%NU>RYasjv|L*%Uee*b2H-E+3cZr2dk=P{G94T6Km^40{ z=mxq-oa-(8V}M}V+5&pblUdeLB?#}Y0rrnp(zX9)BMUsu_3r== z7lwR7vvO@bR}(JeNFfy$_Lj%f)i)=eX@G^U8~5t7bhbgm*VRH`DWbsH)hf|H#NOpu z<+ovIqTnr>oTqp*r`oR--ib};nRh@Z_2Dw2CbTQa)UOY@-bcRkr%==TeY$V2N;+6oTND-};h0-(%t`3ydcEBDbmuqVm}hpx#6>FwAC-njKH8bmv0pdF z!=l|xp#K=HlAG5Ya;NQ5}{QN^z zqV#i&j0WbcH}2~5RI#tFVpNe^sz+nrembc(nQpGGU(4l6VY6ZQf}2l6Ka!l%0_TQA z-MkbvVgLDDtaN3430XFMxdE(hUSXMNvV2P{;cIL0R|qSlgK-~KEv=e6je2y;KKI=* zG6fPGyT5w`SmJWl2lkq90}TpQMIZu!eQ{Ck2FPBL4j7uUC;y-a+!B!yrO9W`Ap zLf9pvRiEvh5;gIwmBI{o5I0TFa@TM_w^T3ece{Vb=g(+&`c4;lS%wAXMUWIeZ@YY( zD!h~~v_K|b&sSiDSi7;@jiQ+D&zd!ztKVujRQcE(RnlRl-qmkz6Ahzm9b)bDe(YPS zug*>{N~L;{brLPV0jgnmV2H9#8Op}JupR*g|N9Uvoea@az5mY-(W{>xW-B(LHWRF^ zUQg+)+$ttnB_)lZqT`zi!3(p7^Zq}Ut6NwX$2ExAT%GcUT!g9f9I3d;$bnWNYqL!{ zd*3%KmtW1Sr`5@PX*L7bO!41)^Ng8vV-4$ZeC{-=-fjR3zPi$e0~aa!NAfk)pkyyT z(GY#?kS6oy*>_KKc(G-^V^`@UU;BU-PL@X^;*vwnRILtEPDcNA3*%Hfg#lSZ@7zb1 zGcPS>n)09VQpZ&9%C)!L-P1GHWop1bCx2LTO0|o?0b_br{R`wCH^Zd6jr!4iiSr2f zwc@qslLUdw2RS*}h~rPvhyu)?C&l;uD3@i~bN+g44CoqiMV2e*eyLNlfLC^8nZy6e ztOlE_*RysOS&1=~sKX!NEG>a0mSnCVkuN}Z+$eaR^j#+JxV+BqzTI(rY3>JLXZV5A zhw`#mgFnAtRGb#Fbg1mF3mySA8fpeJmzT+J?#qx(8g4tcnOm}V*S1Mx6iBv9xfh8O zg#S5Q)7kkqbK@#J5vTJ%IH<$!bs&Z)(X;2HR*VTh)F?apz3ty zHP@5@HgR~eL4&0yaWGw3_w?TS8<~Q$uEq`~QHBbXtQ?(JzD!@v-XL!-Nm5!O{yKX;3Y6>aU7s6)^~d)m=`bhV;?A84 z6OeXz>=@}spNHkQ9yXwJ8QGZ$I6qsumRgx?f@>m%j=vLZIOs>MG+YRNja-vvsp@?h z{0F1eT7+UVpb#n-;G6_1YKeR0bShwCelF`|(THM(9Z zA|05yX7f}YNwfgvyvo4?Op@Y}(pX_sCNPEc7FCN!mxST4HL$-0@T0qv@T{*LcVh|`4_O;*mVI3yMs5oeGw zFT_i6Rnhm_h(rEC=EW3Tf&qK<{bIQRRVj>a_yhw*YQQMCTC?}&;THPIdYOFflV}ej zATY$1S&?&l5OaV@BRp}jlIcl*y@a>sK8Xl)LnojBMeQs#JwB3IkppeGJ{E}XlsNIikmXJg(} z=|>xV+MyzNIA>1;3Uo$TL2eP3bE@C7Vva);wQ{^d*@NVfoeXiJSvifji73kKo2an> zS+6e)?^Tg-#)~&~)4%$Cr@SfuZCsCK3a^>_^HSj2ix*=+{YfY!F$LxFxX&-U8dMKZ2NpSUZq$$pgdl)UYti>cj^J|DlFmyE#bkLFC{jpYKFCEYx-D8~@>+y0;o{4QVA(W+~N z^x%fbNxHGJ=u60WeaC~38$6CnXRQZbvj+K5O3rG2h`HD_*>db*^flWtn*(pzx@B|o z&jBC?3}MWSsch6<1zN$Q;;6sLd=zzU({gJq`vP7X-wZ?9S*XCdZrr&fSITS&!Yush z$KCG`#z}n#TSnSGJfpKYznHPi_h-m;k)q}K_0p`8pxD@J_-CGjkej@m1&jhy;50R! zprQ_GpGlpjv+Us2ZAc6LFq3FhHvlWfun*UzF`AGZITp#hK5<@=%3 zTOQO+6$6u~?fu`VS4YUpkGW7^sT_0}2-vt3O$sSTHM5?zjQzTllFTw~tFOOFV?Ccy z;S~3yciVn6pPpI5KnM6(w%5CGlw$ZL@>=g87z)*-tpX2xO@WB&Xcm3i3co`8g12Rk z!JIXkeiBGFKgzbzw>v@V)`F00(;e~!1&;3si@whOJ{2@^2trwc zP)^!>*oDQ}z5m@f_;dHn{BAFT+^aA9d%dAs*vZ;2$XFoV_Mq$26q{nC#fbMUX4=J8 zRph#m#^Z=r9-)C3)Q*HD8B!S#1yiUMaBo2sk8klBuS_i;;ekqp0mk$g@+xI)>3P){&$C_&E0b8@fvf#Sf9=gz zl?|(Jp-ra%-Qs)hRZHCs9?=5#23RpLf0hoXzp6Bg#x)?Wh;Y5r_J`G`d2PH=2aC&m zAW#X-B;WF_&uDg$625MtQ&y9(27NB!b0yI<;nuA1$K;Z>dsI_9`RwOi=}~7dUrQCN zBnkQX|L|`CWTFv50kWOEWiCxg-ZEj#iK`Rj%H)Rwj^i8KuHfe$q4*7Y% z*n;|7P3rgwG~EKSsy<4r2Q->a`jba)0eai-!jj*G_1Zl$bo>4-ZFn4{Ryl%p!I#r! zXjc|M*Or+DTo=#^Zg|JT@njStFE5M^ipXgaK_#acdkE#q4(*covf#z>?cY*>2c?@#}v z8Ob{teu$EG;-ld{aIzAly>0|7?^SAxSqlQRkSA2%JtO3S(aj#MGR?~o-CQL>`-St5 z0N?cH%J(;$dj_o@4O=bXG)peSDd!&LP#uR5?#@Njm(0`Ds~g%|j@74gMDJIxy!5Ia zcQLVx+i^@deBkH&gLo8x`4f;H_DQ^B;e1C|x%Aj(+sNkJPSoFzfcu%JY2x1J?zo1Fo4mR^%J?$lIWSG;lL zqg0%LOll0J)zQV|nNt8cbKE=R(K`rbFP#ch7BJRJV`+)YF5Ao7_o}Y}+T=)bVaIym zNZtL{ruz@((M?WSh#M;nM0uY!i3{h{s-u%VL@I|p_E8VrT0S-W@o%{QW7EZsPk^QU zbtNMic?0l=}wY!mB9Lb>> z`o~Q@rPcISU;L*~6EW3EgPh}4OCd@Za46A9!<#hbovW6%PB4Lss$QwIkE_q{DLA_h zsT|g*TWPkC%7jUxU0nIMw5R;ITLcfJW&VuDK%Pm2L8m^z8|nwsj@^&NbuQ*Fbv5J5 z1@sL&zXEFxw9`cD@BNyufA-mzUOi~99lhjpAi#n6kv`Hec?RyX7*N9U@znZzvs|es z=oJe}sH5{br!k!bmPIlohx+m?0g`54_r$lpm9Yrdq@}8QLIv*uZS+O`?!%2Ap9qs_ zZnoZwYohOOsM{#iVa=7HL z&E0gpTP%9!1^xaxer8?<;n-~L2L~hV8JAlFY)@T!CvYOo{)!bwfIs{;`D`q66iUO$ zCDNlWOd10w#g6;=10~^orJX-_cH(91^Aq36z;9)ZRSc2Gtx66mq_hs?E)H-E<$O85 zg(&*v!Y!0J{#*T3(ew2;{u6_OFumMLEK|;oNKW2 zNQ3q54j25V6cJHE@RStewUvf`x=XMZ%2@dRNmJ#|^rwb;I+i%ZwTa%_s4C0`)O*Fkoqv zSTvf?7)J(#>;k_thcZQz42W?^N`Vif7v+)pn%lXD_Nc-K$iEME#^}d1!+u)4M36k! zdFN!TYT^YSc!f{+=t24*5e0J~t>0zmj%JZZvwt6A{vLfvKejjae%a|CeDmXe@y-w< z1KVCSqh@rW+fP&}^(*?=L1FHnLd8`iW%b9UUwI8gA>6D%y-jdHxww-z#V!kDK7!D` zMe#%b@w@0)tb+)BeeqihHuI@bo~@@EZ428~=MNNxV(&?Xn;OrmZa&p{lduUK`8U!i zVYA>yz+SgwrtltzaM%Av)mOMR8Gii_7@Y!=N(rO86&RhPl@O#Gq?O)iM35RW1`;AA zAf*z5ba#s)xeC5~u6Z0K8loAq!?&&;yR{#1pB^r0;e;*hBq_@)5^c!ws4QFVVdW?F^7;;CX&hLoC zPn8z;kA(Tp2=sb59XIUEal&Gk^W&#Q%LSgTc@Ppg6rzOw{zD6Q6M6lwXuFq3fB)rc z3HJG=h|Z<5{d2J`o6ZHBna)2F%AV;n4_9s8uj0@>GUz4P)lyhO*s^QhkE29+l=7mX zU-+B?xuoY)CgxI^hl`rsrhKx0Deut4K(TqSd|L7V=g>IzmGxJvyRUo$Qfvw%)lPzO z%#Z&Lsr+f={2RbIlH`27R19@Um0plwT5(T-EGS$V(ab=R5Q63BrDIgLbGzQ z*uo@8#q0VWyes&iehwG1c|Bja>%2mV>?c!gE@O!*ze8?_mLZ3`54Mq9?)*Ma`+fec z{_0yo#?J6bfn%aTexQ*8fo&Ofj8vqRPIt92xFAxS-=%j9^0WA6`AT~1b3rkNTSE>k ztrtX-L2lU8={Uz`2&h{WSRm3E4lo;#$jLe7^Fg?>o3+rZU;exC;@B+ljlC1! z>%;d-pD}on^!M+5OD_Ceq$X9Ctc@yO&YMS7zm!+ElqL3DpP1)`qFTO?oXG!8SS+!= zrLWD41UfFAUyM0@_w9P|ubl9Du{jAU7351!T>xS57n@&n^bkK#n=;eM4&5h-o+jdk zd^6=Q&tt{l`qs+ApTu=rrJ(rgj#xIN0`b)h!2Hx*;olxw@8c*q>6mWKGi9%5-<~dHtB=jsr9K2? zv?#s~#k|?=dX77nBKWF(L`ecZj{^@AAsM=_!THQkq~|CdHHTa=4#f`}t&DuB^p7-q z@)_?`Dc?Tx3iPx|X<=__vN)vZAg=8U&Cps~6!tU$F~%?sIrF$VaQs6n*1oS$W}DsB zDqTf&A!P4-UADDziYqGaD$^)I(0?@as=9y2Mbf<9M{{3d?$+G$PRNCj_@PjB`f&|u zMp@gB>pv~-RVO__Z5qyzbR7gDduIdu?WVGAgy{xfNHI6hsEyqHVP1UkaIk?2EqJ|o z6%-Dg2n)!I{|0R+(*NSvHv~L?JJ=FA)^bFTE{XV}zyTD&xjAJs<7xlFp4Wk1yVwZ4 zyz9S8aVpQf3tyd9@HI-mP_3R*((yPo6xZI5wI=BvnEJi<749A&uBj*jIP0D*urY*S zC&woK<#u43^{UR`r~dM=XFGmW6le*CzF_T}qaJ~xudzLO^+uQ!8%|sgXGbe@LP)+6 z5OplZrebhbZhujZcN46CD{lOn@d*9d>Gz4s?>>#H5p&<}iXt~YDgf%#9R-VRd%jQ0 zTXBke-6ek6BtFK#+Me?FMWoP6fxFqg2c+pg`b;^`Q7l6&Hh9IPP;0Zp72`{H{$dUh ziGl?WhxZoZ1-CxFOXf!`o_5PT8WOR&NGV>Mb6#={NZ(=U3U^RM{(BG-1_o?DWayNP z`E{4=cBD5(&hJF7qrs*9Y=nGKPo<~3U+}xSeFw~F>i3^k9>J-EVmD+OCG|OSE@Wn| zcG&zibo+PgJ#g{f^B&Jg-!1J}x|1vxknGeN)_XVKETB?~#p0AUY*-_d`Art-bcg0Y zlj)$ld!%3dpa1>5_S*eXhqa@jgKJ47+7il_p6{=CA8IsJ|Dh>Aj?G(U+u^mDvf-JG zpxl5-*$AbYp+$lOVbdhuT+gxhbm9J4CfCtuJnh>Pxy-hP0SVXL?>4KLT&{Z8YoFjf z7})9wVIYP+rR938azE8dpJFqJRyv&#{qH~`caxw3=>u9u_FwdSC${(_d-*#~4Z4E= zc6C#rEVXyxI^c^L91VF(K39Fv>wS?eB{z-cw;eyecis(Q!V5XvXc|j>ZSA6SzS1i` z)w8X~K2=3>5X&|q(pShzP+u-TwRHZ9__7lbn7os-@GpSQv+Ho~CeDshBak^)#rGp+ zOF-eFAdYJ?e%dr8&UIy`ve<#PtDi0wVo9M?`gPlEQ}&0w(4UpipYqTx`HuVJ(?Hqx zraorO4;v8GmUdOc=BRml9Iog+ZTLiAu@V|kGC_OkC=cc`&~wC~C?Qen>hbw$v+oqY z-6Uba;G2QVoo}yT=`}=uU$`|p%j}?!8XvpejF&yQ2V2Z7zQGpnFchxg|6(HbgU}an zx3OZ+ihgMz=f_giY7(Do#P7JRh5KFbE~4Vm`~r;ZHu&Eu{bt1t71?A74P;k6HCv*U zzNxlcYZdQ-0xP&CvH3Y#3DBdhj#uUxtbG z!Bfoq%)g80PChG(k>@$ra!zllo5DG$E0Db&zGqT4W9F~-h+l(aAUW6K@NCaqoHA1# zrJLeW)vVuTPg$^v4)l%ZIVEYZ^@D=A5w z+A$i>#&(0o#G=$Tpi&`s`7wx8N}P|4`X_mYi(^H#9qNNo*o9d5-MsLg=|bG&bVm@q;t^G*cq}4rvCgbRj5#Mr zybxKQFQpcZTO6vYpv1To@gl319c2Prbw*v2W1+boTPvG$z%+u1vwGOoCeP7N9;F7s z@7ev2CsanoQ@ z)_NY$1JRi#+Sc(*jJ<~6mAlLqluJHWWx$+`iXv2yI4qoe^4;Gg^ zv%$r4pY-gw-Rb3rVfc4=q@YD!$5aDbn`JJxuQh~@8U%$B%hSYLvEI{R3fh`wo;D$x z`ICT@WEcg)RylMwzR8ZzT8#!_Gvmaxs@ zK!1#YDE(%(5k5~N99^mG(45h{!gXFVwHFruKFK3a+A8O){f#HL`ZH$pfW$|pSBUBl zIAv|V&hIwOGjNUcLd-jdJzrx^wWhL$|Ngo!^QChqPlS*B4*wJaICr~40gJDOEJvy< z4KOZWd6;V^;=b6p-YUQC51EW2G!dK7r3ac&S`_Eld!Zj#1(Vg2h87N8k_>V@8HH@f zMwbx_=UIk7${Jw^!+E(eqWy4#ejmPrcjHuE3?#{i(b={7$LJ)*4{C0(bMWPwOlwJN z7*k2vvr3-)``2=Do-$THo_iX}oBKCmR}^vnA)~N)@OAy$!Z$QHzjrK*@37_jQo+}l zf$7>veNSyQF?Zk>It`WR6rGzPl5YCG>P?q^u|)^CqfpP(wnZ9@f>Ojh+_TP|K$U2j_v57kjK$EgY z6>ZrkEHOUN1G7&r;4YZA>Gp=)pVXrVLU>%IO`4+P4jH7;ET^-Y6EhS3=1gJZ0X*p= zm>axS$SrX-Yuc%3PoYiLi8z+*Cv6Jm?m$AtIs^FHqVMaNqzKMF(K8p-Nu=H5eZXU4 zW0%8?X0hJIkU$PpX7djyRg61XPIQFt$kH_-3a`+(k&kc=JY=AIWI-%JzPa$Wc(KlJ z{fI304ug>>|9)S*==dD5qE0+cu5U2Q}R9U^m9J^`7F_M+rA|3yc#m;S|N{)!!bmQBo{`BX^hK=+7XO$ zm!4LPJv3sann2g=8fq()y?PqJoMF%aJhmyidseDNH1w}=uwx!7$yL}Re!#V$(mT@5 z=rB^u$-LPNjTCcBd{B9Z|6I`Thx@c((n@<-W4t2Bzh;V`DQsI{o$E$zEe=QegH|XY z=}!symENBSs`PJheB;?PLd1oRNNmA)V1q<06!S&Bfn)v?6#Fmy3F^VJ)5QsTf- zo1qb~s|9TZN6paU-B)~a_hZ~i1?G4mlY*oQ0wOpkns&~I$PqP$hgp8j$}hlGO7}Po zrD22g`w}T%)!tP@R<}V<&ro0z3r_woBR1ya6r|Xo;`Qo-%Q@VxN!Q?rvFpnn$-@Vo za}STO7Vj-e>iqM}Qi1FqJ?VK_(iX+hGZ~qRH zZI^29fD_(>=7I{rgZ5s8KI=J$;Vo&Qza!QPYN*z%(9G@KC%-5_#GGyGmrUWun=yVe z;Ru82gf2Hzx^t6%{jH>XM=I<8I$En6cUL#Ump*gE;hM(8qzP_Rm)xP-?iZr3^rT!_ ze0K;*PW~KJQ;I= zd>{{@!wnAKu#4e--Bo|tUZ0{37%Gr;WrISJKkWDob>iFfur4IKNa?QUAO5$a#%LQ+eg<(x)}<$sh$nj^(2b6hZj73)>QsgBiydj?&dw2TFcHY2_+R*=&V>&5;*IeDUQi1a-G7~-BkHb+yvJ~72ZRQqR?$CoC z;d5@AZ!>|Cx&Qpp6PM@qKJ;{7C64$T%anB!%(`KHIMJB6UOD3+gW~B8uemhewT1`S%28+>~6Z@f~K|kV)=|_E3Zk!B!^u_TI zR~xy=Rau0ohfrm4FG}2)*kSM^Nxq6vHoo8ffuagFPS>r;Q!t_iHN^1p?=~H|=Y%b8 zyNQY{XX|S_Hg3mGW53<^EsLhj_-2T` z3muuRhnY80vEOOQR1+-64fHt%-h5DelFIHwkI7>J(JxoT{;0BzlDi28>NU5{nMM^h zyFYy6sq{AgApp?R&F?drcHH(Al9bl`$@jM;j~c$n9{?L_$wZhM%@;1piOxIxmjw7cA? z^})&p4aL0D(-=}EL^ee@D<6Cmu=h%#;Fi$18yoPvIxkSJ%i*rttjJ;T!>OO#gH&~{ zs;iUse@J!%ZdKQR@cPi}Kxl9Sc}w1Z&pjI13#n%F4$nje=6%9GsN3A!}})Zq_;0C`t~$&dcD-@5U~8U&L0H;MM*j z4RZ)h*zgUeLNRSBZMb+%=h)|t6vUW#S0Qctc6k=kNnXe@sW9HZV(J!kU;|)pOGd*x zXZSnzL3Jui*B9KToV{yCOgiOJl(l2ZJ^j09n;%)bRQVE%&$cKjhVN6r8~*cr8Bg^u z*A!vYhEJV}6p&+N$0cq*)VPn>;<_$lyV;33Rw5WUmS z4Jkc}m#^$PH`~C^b$EW0G+6M4<>!U5LPcMTx2`|?cZuwp%Xpxx5o)`OPFIU&@md9* z(2%geMiQ6!(*z{vZN0GV5-sJ!4(IRw{y2Qzl2B|Z7&*q!-DBoN0n(V5;yCw;R|@EV z8K_-snjxAwjnc_IC$~S7$RG)smgRd}Em7b}cZY3aTn3KCiR74mq9Qg! zd=#UE0rK6SYI-}4bc_7liMu5;=Q6z_Np7MM#Lh<&24cr#!447rZ>_tYhEcV@l_|(# zl$?}sKiSuvI0srw9@4*7Qj5(8Z&GWEr}OCKP!bv5BZ2$(`hVDq3ITmK{r0n#Vjj-P zvR1y8{vmnClzr(UgKXDb6hg8uvlM#W>#%KbgU~khm>ka}@^~X=6OXOs>eNMg`RIQS z8uPsE5I*ciR#KZSX^QIqAmJhESg?qPxe)~Ce;kK{SrM*@Hnq|oQg*|O6yF7Y8WR>? z^ApwKUjXR0+TVn!V2QCJ>|wy{j<_-mj5` zxxSahgL67{V;AA%25*d+cGTkfn-UOx9s) zP{_T(51J;KGhlmp&}7-yU}*s;hHH*;9wR(XN7?nIH?3Soa(HKh-^p$yv1lYdzci=P ztvxg?Xx=4gzo{E&3F(aH&n2y6jj+tvi@YUxK4zaWW@bL|~*e zX2~T=4t51L*BZ3^E2U?Zv4i?c=b~bKbF@QUbh)yFQBC61TNlIYBju43>W$jIE=hJ9 z7V}uI`mYf=WMJU>Xx347DG>0Vn&!v1Sn-q}ZKOLF0*F z?hAEqLldo@|J4Er`s;orS3X|piXkddtQ$IyG3BM|LgzKycj`P@*8US54@yNLbs3vy+|gd#0|DJn)xx=7*}!!W|de-PKi#4U$Uy@dL0t zPSCbSz0$+Y7*o5R`R?wiWPuYK&YmWoBc?wf`8M*Yp0)kX)f8i2WTK#9$Hhpx(xrWO zur+4M@~av&lAi>tgqssyo|b0Kf1*5FFet}Lpqc01G}8P`@MQQ_yfqO*$%?&rxLp`f z_ExR>OL)CPzWA(|13?CgFgkV{D+|L4l*dcQ<2)vZ?crxiUE50g7fGsn)ZrvInniF< zW)IiZKeV(7XjG#h;v9=%Dz2^clxlZ>WL#&EM$rmEF;zU;{?+65x zCqZnTp;=yDcy-<;X^NHqdBRBBdOWkm)5)C;d3d>8FltVy8HBFR7kgE_!VtXSm-=@^ z2NG!=qr(q(#NdSx=T9778(&gvn?9&7@6@~X$*{U8+1};Z1yDC5h%sysOD*!>Egvqn z{O`JD#f@CHo)H?aoIhY_CQ6?Ctux_|>u0K0Jz7v^P*2Kl!Y%J&o-x@``6rCNl$a7{ z*0h@qeMK~7Y4-0hhg{ee0d(iNBl!9wH27C62BT-DxO(H|L=0?u*H zoeg0!yttwLq)uDDLWk9YSHIaN=|m{4W~JJ&jb7c*WWC+-v}Vyx9*BW~qezwf8ZUCJ zvdTRp9eY;3Eg>kUMS=JjoxtfD8jqA5$`FiX$zKpQHVlmqgN|8jAjX1qif{_Eh0)Hq zZUL?Nf$fL-9#3jVXvrt;XmLE~>Ty#2Z$d}|+0JJQGrK_hK^SIr0{b7H7Vp)r4Q}jz zTyuZ$wA!~*d0d^>@3SeDX@giC>00;Hm%`2jLobV3(kl2BCJtZ((xbihT&LN3IZORq z#Yvp@U$lnGanc|FRTr z&6>3*&~zf~XeU=mANB_}48*#V$xIDlZEi%YZzV|ONGHvH_P9RYr{VY}eUViEN`JCg z&QySUt!!H4eyrh%h)n`9d1#E`^&1E#4#zb$4BOz|RO*t+tM$pXA`E{yVK(}b#JW4| zd_Y|GP*S)btRZYXar$`jH)^@PAIu8sB9+Bw;_+-iYzvOw!ni?-mrU{H0nV=SXp@Ru zx0CVyy`)UZ*|>&|z=zTd$R=J7k#KI~q67lGfB`^z+D2_r^{vT#MvMAYt9ZvYATuBn zlFL?B%-twcni4t~VFPipprKv!4iSVNMPVlqdD#McAe+|YY6$q#s|0JCxb6evG@8)3 zaLwTpE0cGY$y8jg3=Iu_lf$W_w{+fq1h5F07e8~PBcUCz3PWc_*Si|Sn%?b~C$e>C zHFF21Qpc3cXAdArIx#4jmtwn~y&C3_FE#|c>3ulWn!~UK+xQXM($eZo!NG-u3UwA_ z#Jo|lteTafggJcNatw}UR?LYMvooJM1F`FJTw{{Z&+1FoY_@auPfZqY-loqE}g2Rg(TDmzhGv>cbD&v3z z=+px7UXIYGJN5DN+kPTjqvEQD4!{%3vOG3gP3jaDP6l%Zq6wGyW$>&Qb(~q)pSVJ; z%C-sckRVIccJwo;F-7m%V>r2#1*-=eGj%a%rS=x{r~WEQn78aaWA&^!65pg+JsZ7V z`VlE(TKRDCZrH78(cwMz&^^v4PBqEee4O(pDrJc{{Qa4`<$%Tyoa`}KrmL4YhSid; zD_pz$@f&(~Sky1f;ESnd&{%yS^Y7pcF8>qQ<@dEB_qo~CU%nw*b$~B&1PV~Pc@uNq z%A#;IfXux4?u``@@2(wL!5@g4>EsvsI~vxdI(oKVFCSXZmHai;WmCD;JGGo#30ic` zdm?wTBVUU<)D`!FvgRtbXwd?ykcC$x6m{*;v^W3%7;+SRi&lbHx zi8SH$GyZ|tr|+?>Rs{2f-q%$u5}^W-&ehXa_@OWiubACmbW00PkB4Fb=5Y;Q*KV%x zV~paf<>ERJgIMkDsVU*m9JkuiKb)(kB*|qlT~Np0W4Uyar?t4E9qzQJXglyfs=RE zD-YfjB^Kyp(&wRZZ}Kh9Ll`6Iy~}*CGt9TRi&*9D>*#uK;`x=-qHtMbOvV;6$Cbf% z*bMkSs#9q<%EdR?5o@)fY(a<$s+)siPf;0s5|fjMQ-&kI;~KTI98U9z8b%`3?_U?- zj#wU(AO~;zGWu#Ynw=~4D76Rb0^*zvQUCwy0iU=e%SXKTI0@juw%9y?R|3Qw@SJ*E zoXVAtT^SN?z$K9zMZrHdphR)(z-2v&k5A=L5<{ZDBlTvl#xdiq4)LZb!MGP6m`tvD zYHkU@?O!PkzNB`%ii{~T-Sv?$q~p#W1@a0%H16PS6-JpcaED2~RT9WfUrfOyff1?! z&53K;q-6{3KoGh9nE)*X<;~k!S7U$l8@YQV5}UoobCUP#S|jj;BDKJFA5-NDoYI#a~J*EeDHsQ@2jjVO;a4f-TdJ@@Liu_%nKO-wU1VRY@aMzCMWZ{Pq z3=DuWQeTW;TB%}3P~hNDEmuenAvE6Blh zBWc(`RXVUe%aJL^&Ag@^nF`^HLh$?dw+$t}qKc8W4Huj7#{QT4@;w!RFZN8E2Bx8| zk1DDMt>9n)p}0|%Aoz#~d_=%`L@=t5sI$#Ky!z+q8WUXK&gh`Zc&A?62zNMkN>Os; z4F2PcYrLa-4i{DWSg4!d$4nLzTSN~P%G?LKNKA!I1_hI+`-xEyGDV$tQ>xb2ZX_%h z{wD||{u>0#nm?gF5aevwxt1Aoz-UTV1=~I*Qxk2m>ZJ~1`2Zqx9h*89^XZt!|Aggjl58dy=LB$^J^XNELruecqvQeTcup9n=E$>w zJLbFz_@XD5Gbao!GcPO6hFr+Ng_E9f$onT z1W-fG*i?bG>)h67u+JBE{S5vjI*M!`Ghz@`XpX2r0W*eWb6RkB{2D*0pC{;`4uoZQ z_Bg9Q>9UwGy{N#Mo2w-DWuKMMLA?_kca$c@1J9+;W%oGjIFPrBdcd8%oQxp)-XP2r zRm+FD_M-y4JkjCCfWs4fT$+bE%2iSZ8b%UB>T6R};Tw{f?NgHEiDvJ1P|U`!e`hnJ z#k#TE^$o*#Kdkz#&)L&oK!lBvO8h+S-BQ-uW1O{k3TegLkMG{9SUFIV0M=MMglcSS z5ds0vO}xA&ePo}_%Qyn`lJ(WD-Y~L)4N-hBS)_mE?jtsjRPQJ!H08Gsf3>=Mqg{tS zx|gZM9Oewi;-ML-p|#&nKBLovD%koS`bM?j8gqUvQZf)LYhAzDO?!6B<6Xbj+-5BO zL|Vvcki(CsW+`f(*FPU^)4O@8i#fn&^k3y5)n5R!8D)eriQhs1CAXu;k^RnE)seL~ zJvOOYhMD@T!s_wE9zJH?j|8DMeR6uIFHJsG(WCnHGo|y&6`L34O$}o2@o^CU#8We; z%+etQ5c52jc#IAneYll&_2Gs<_feEn6v4Q??>o-p7di;~ouwE2f@#ib#x#4keo8_f z=ivt22hd;7dw>v3oh20_{#Ljdx&W_eA~Uuq;N`L!yRd6xl1=i+ulAf$S(cl*VZ@_+ zP~VU{Uba^)HQCfyEzJ0+3lIIK-F*9w^rwI<2U5TEY-(016iOtHn|&OYY_C=?7#9yl zl+MKW$3q0EdlvW?Cvay(+o7i2h%BEAHUsPEk@$xhFe8e)%tkqjnh&E3dH%bdw|YxD z&c4mrCv*w$HIT{hWxv|qe537#!b5afMV({>5vS$36zH3Ltc~ASqJ=wKJsi!XQg`=@ z?^AS}y-?6QSRj++Ht(&90mjg?BV6KWS_29@O&TEQ1(;Mn-yyqE@#eb^CGM)w~Me)1zy zqw|Pf4}i%D-2Qt0wqfQxQo*kFVv;jntsv(!shVrK+amMq;}ub*8h8a z?w3d}Y~Bewht;yafF26qg23*l=?SkVfQ5h(axHd<)HFSOfM@xs$2ZN`+RjHW4cu=y zl&k*uMh144Vt~+{j<@aRg+GFNQE=B9)`Lcm%pCPiRX33)i5KFm2qBtG+C3$ z{+iFEe$!~CMrJy?R;BU!@1#Aw#LW|EWm&*>ec|n+aoGOYnQpY0DR)U@Tj32L?>#etJbV>1gENQwqre!#hUkCcXQKj}Ts`6|N zb;CSz6Au><&lhjdCm|1@KdIBRK}mXs@TTF<9A9bQh3kc6P*@g8B%dx-fULQV>o(n$ z9d~7`ZIMzCbr}Qm5O1b?Vev7xWC_VVLecld?|N@TGdQgP*C3!x(I+4PHMn=IIOy%r zHa8y17^T?7jKN3r3>v=devFK$P{LE7!u9#Ns z$mY$8XC4U~n6vT{Wz5jrs|LN(_}KWXdaKN{{CE#H>5g&cUz{3wfQJf3z315PRJd2c zB#3c5zP)TIe%ze=P@1oPFg(M)d-Za(XoIG(YiFc5=%_v5ZYcS-N=PKYgz02hlhz=Rq-^!ZB@@c3if@lCM85m^BlyVF^~u z+=~Q#u_JsjyTLUM3VkW@^W;e9VivM-%(;|)L}6isNCXYGCaQ_F9f zX`X`*A1`4wGnx5g)0>iOK-;=&MW*9a+(yW)6MDCBX#&(TqXhZP_;##4hO`af#!v&W zpa&^Pf4iq%jW55avJ(t@MXyWnx_mO2x*CRewzO{ zyjfN%;PeNu=2k@Oh5o`)f7n_!(1esp^f|h!wl@~T$e-_g5jW^X(r)vYt8T#wBph!hS+)5GUoRX-QVvxcS9~YpQbKXlZd7$Xp}psR z)n~uxxCbr%8^&oz?3v{_ic&Ne?SCkS!zDkt-qz>Mvxj2wH%AMG#G}U2{PQo0VNxm%*kaHkdnOhWLkdbz*5UR{-972`i z%eA?{T+n*2XqsFfWx@K%$<%esb2;u0HLte}ZlXnEFs07mgFcmUKh8Hm3Gx=)wvRH_ zx=+y@*B@JP?rx4YoAQqyurY=@MqK6uSs>zXwodX~lW5s6b<_Wh6c8b-{CmGDHyUxv zzra6D(=qcoA~Z1DG|f3)8p~9m;&;%h0UZ~ zDeMRwop!&Mj}w`Hq_s*2(pD9-;STAn@Wb6<+~s?GR}3jF$N{LoVGE2-jT(5!cj6f!C&d=n{?j}=&-nX{75>- zoaxiTF5($s?n#8)&9ia4c9dQvhm_OA zWt;m*{yeTfd;E{XafqKkudb8GcaIXV>*`#0a9n~v5kpwKif?1~QBZ~zfrZw4(i>s= z1L&9H>p_Ruj;Ze*6W@MaeZJaDhck^naw2oF_@dMsKlc8^)N@* zh{1OK_0Rgrq1f}8@BapHiPARR7n3+^PEPEw4u&XC3aWS2Q zS$IMM<1})yTHR6kyJ6foQ~n>^`X%6ap5(R9=ug&m93F<$@8LK;y>l%Vrw!W zBpoOMs9$CoBcRiyQFQQ&e}PokWyEtD~J&uq63G2 ziuQ3~#g1F^i&(%qv%$1ADYEr?=a*HfkrwMkALw`CDtmC2(A3BxwhWfT)2^+4;Iv5v ziMou{?W&xf68S>?0?5mMIC!NlheC;2nVIjkw^RSn-W<`ob7P3!N%*%pYWok076vdG zi^=<)C;GkEQiB=^Y{h?kp^kyLLpi&~sJos_20B`_o_zm{_cZdG0-|J z_f_a&&_=1-hqaTUzb+3W=6RGUy)sy_xDRAY6_rY8!80n_g3##USZ#2X2n8Vc=O?dl;bqL6&$9mP}H1gd3V zlTxhXn*fB!U0*`1qF~~kMix`5h)+!Gisp>($!fVZU0g6alL3x<&X6XCFUPO^AjKcH z23}vI;Fot~;Vho@y z`jd5cOQ4r`0|_?o(_BBZ+j;g4*JrGWk$hulYx2&~c}nx1vBl%Uuu`{(rcxB2gnt)sq8$Y!o~ z|8&iE?dg;>yawKr5MD9jCgLGIls3pC221%)r{W4nZhvJ1}bq%-kg5PET;E-z42I_Tr7)u zMs;yuV3Y>#e)ZGyKNd$z6@xJBdbyG;oD*O}`TkSttJQ}AQ9JJ@`rW_VGG?Hs8XVUK zrZIti_W|Trd-4OzejjKutY`_W=$yFi5-U=1DKO)kD|9FbzIDkYMrUWAs#`Dh z2)oy{6#8n(r1{J>84>X~pSAqJ(>my@!RN{!J54o8%^ZRcU?kEJk1q_NiNztO*N*%- zv6OZ6?_@|^OG|=U51S(uNv&5zfUZ6zj3YfwSL;aPtO;6SmOTEIwJki$CC6?r$4Q&c zk8lx)mdmBIwqYK;O-wyTCWh*spk$|duS3NxMqOLz(+SZt`m|!F(mKtM`Gn`UFKc&g z)t{*YzVs!CRdJ{3)2{ZTSOcogo@W~e;Vi3b6-BQ5%LmiT0J9@X8P>< zet=+FxKD4x|La17K7*z56^_a8m}zA3wV}l&`Z!8K3~BorJ7zJF!?fA|_Q*T7UfV)$vu54#Ts$=7A`S7qtgZYo!ECm|v6<-%f!TKWqrJ zMy;+9EhYR=*#gp&%g(%ousEb;mpSimLJRm(Zbuus8Cc8&X*xTyWRSBz^jN^RuH6;V zMc#U`fzzlNqF+kM9y+i(1glg=TSKm7@naI+xWu^$*UZd5l+(Q-IYj536E=Dc@hf8w z2w!)gdy$uafUr@~!6S7iG^Ei>mVP+qM*_Pi34P1M1K6%)qAWJFbkf~S zL2iF-Djh>c3(NTX6m8GwCo>?bOM2QVSLWi9g$M=IU~ckt_KAql0Vj~%TZ|6mML--k7qGYF(sIj8q_ z+#AeOXY8IIeq#3n3m5tvV8k%h(ij+4PRb(WW7ImgvnSjacbpjgh(0J+l$F6-#5r1e zpjs`Rgn5yA7>p-*z6^T;-lvR?J!p11EMgy~T^{ zc7x$wp+qF{3-WH#>3Io*7)7c@cH-nIh3s)C92TEtl=k+Jq;?LPh%aD8H;z z9z4nxnd|6~ym5`h6J1(Fl!?Xor~mnuiKS)NqPFs7t6r0{dkfI&L=$X}2dZuE{aHO5 z(2brjAP~xVyX- zVe9A-sI}oKUTMrQ7at=EpeBk)1AfhgOk0iCfe1$vl|zd7r>|g}x41j-Fa$%*UdayD zS1Z4QY8+JNJz6DpQ!EL;GuGbEZ9p!&JRM1}VniPKD{eRp1o5rY6dw0Am53YAm5uq& zhsyrF=YFB86`o-wGqL9^!Xo4`9y7*b*J`!zE#3J+G?9;%e6P*G;b<;~cWQCvgp6FL zKT9?abkF}$5CL$cSlYF@_e)Y8K>jO@!B!x-I1EU^o`8w`XUnq$=!f`3gD_R0-ht<29T+>u=HC z{^m3=j>{)MM{}op50wxPN>YTQ5?!3KD=UC6%X99gC)tXM8Uj@ab{6Qw?`PB~{gs0_ zR&X2&ELZv(Q`a!0e2OVN@5mXVe3g(tFu{Q%(Ta}mO=ggg-ccglmW%#s{o%^ZYSzf`K+XE zYUK|#dC0WA*m$w@=YlfvV*>n*xO8@sy@0b@Rybd5%(XZ{gz954hFUi%2^#B(R^y_R z6yaLkAFK|sDY2@IgY;L`QVccOgX4???%IHnJ_ z9l|iK`kMhR3|}`44B%n?b+>3KXIYT$X4&n?L+ z=vi)8#4Ar(9_8qWISW2n-JX?bmf_EUoW%RF*p67G4tqX&VQapt|yAxDe(x`kG& zJBmu^<+M8hO;oi!sf}j*(;FA`w1Av7#r^w}mUdQoSz~Ghe6$wh7V0iy$4bErQ-!jR zw8^F@_`~7_GQEZ33k{ze`fwu<=7O^t>sUrl1npTA9u*$S=xpG<-bx2F0BFGfDTk{~z_7Ty&1|g$u`T{$+|4&EfatzB}7)?g!eFN4!mdoPi zkuTvG;WEY&yy(F`;9kbFk_gBmfjBE)8DQgpTD+Cc>}v*Gm6_&Hrp-`TY`-Aw< z&NAVRf5N#qYWrr(1cEZ>R~U7ge4vEQ_QcMgnp2nzLz*)0HMqOchmprM=*VpMd-)(~>%y9m_{bA7NZF`tt+R}pN1;h= z6wOtfd-6zcsf16av?@J+!Kus+sBiEE+>s`1HTmfwg8tP>>yVhslAh8dmre*U{A1+P zw~|!0q24D=Ef2P8TWqHFlUeMXkt)JA#6IL5&lwvM@fcoKRHr>sl~WemcHSN{k<@fA zyd$Jt`n~~}Qy^$PwUg`7iqPN8avf5i&2j~u?4mZVsQqZDhH71?H^!w9j^-nQne}2> zBGzFY^HgzJmeDVjy}q-UIXz738I9f{qSMNR4XSTxKNdd;zW2-2yd`t7)18Yj*rjr` zfQTM3L(m}IlXB&95r2LgQDoqxB<=ayMX5mu(?6v{n$>EA%sGohQq)NlS2ShXsRt1> zxzoIt=*VzL2w)(-U1RT@0}@V6r>4Ix+LWT@;*F_?_x8jnLJRjk~VJP*^c%_{Uw zoLz58{({AYO!js<<#S7;< za4BjrZ1hHwO95N$*wX-cwy#!iehKtQvRtj>WINSG)IINsNb?OP>#tw^lhf++He7Z5=b)Q9L--(T&AY$vIRwprEDz6ie}ui)xx<)7H&|Oj-+9&N z&CjI3v9a*1R;|MbwQEKAb!yoV5{%#*J<=(~f{-cVH-Fm0q2zEOlm zKI&#?_H%Q)IU=vc1D(k$m}fc(a*N$H&>D<`sq&)F(`4Ci-Jf{C)V-2hwKI~Z9M{x4 zXO|xl@TOYBq$*jK)seuPy6*!zjSXtVaPrV!tfQ@8(>Jy6jRav^lxwo~vYM#4(dJ&h zc&9!mlJo z5hj5in=>&z?}~7dgr_;Y_P_*K06Qz@vR0pR+VfiZ0t6{BLyb-X6knW30+^G_Na!{| zs`=uiMT}14)v_eiFuQiunQZQjOU?2qZFmIFCQqOo4@76Ua>FKu&|D6BcI<)7!@1&$ zD;UWh3muDZN5P0Pb84>g>G@C?!;&XN48k&Df+KS-_|yfT#?6sOQH0}oogmDYTo?l# zCL?E_!A=lhP7s|?=A^*_>j}cj3BqJ<)LgsU1Qv)X!?u=R88kiIJp>jU_V8rlf&&u5rnwkq9E%bQ zJEz8Qz?iul8clL@lP@PBN-4eB!q)h2G!oa1JtkIK_va?eV8C%%X^pZR#cTrThS^N` zJi!bd7$jwy(um>neI1L3Z2k-?)-JvTrqyg2ejGbnUr2bzNq6zp1OQs88PA;7E~A`f z?qjtIWBbbMI-Yl!6+=`jC!PIvT{pMu-f@tv<=Du9hx8aZz&u7qj^xzMCgX;YQ>9*M zm>s#3LrbE77^YYGXN|4BbiJ*>7M&X?194qovE7mwf?89}DJ)sloSBG6bxa z#mM9%sF=u#A;+>NRkiB?w>GW=lCTc2_eN$IaH$I1I)I7Wlk^BBjLfGHdSu4@M8XmKZ{xl41Vpl@+k7C|yL?+4<5kAKNndZ56$= zD)*@oE_~<%aI#p~>G`gwX5Qu}FynzW%`q&73oedakxSBux=?qfkl8b{vt|E08Qe3& z#x=-0S*SQ!B!d#9JAXd_=f^hzz{WM*^%e{dF_AT%<@?TE`;{+ktS;1)%h5gT?DssMMUUb3y$3F zQnvvxO3OGYv5d1k1vHzU=a6a_VKAdUjjBp=LAb7ZHGyDkA zQ5cjaXHX0l3ciO!3`?M0wGV~gSk;b4}+J~O!Lsp${Ia|p~xv9x=#b_B}fqH zXYPr3)CQiLUp~9jeLYrR&r321(OhxG6|4y!i+MKn+EBs3sK!wSuIZd39;`E3bfo43 zk-!MLY^2$58xaQYI(ya47OqKw!1YvqYFv8J%ZDd8k%*|Pmq`%qsWc4pKK;2 zVC-6101VXiRD6jvBvOjWwtkA<MCce@VLC1k zjT&StOm{%SSj0GF;!<#Lh0%*_nNV!Axx9AbK=ORmZMI{zlgMbM5Y2B`K z!gI0bHX=s=#QWRirTRopACxI*Qu#dCjU0`3p!3iqbI@{!-Jh`$$d z^qza2#=jU|w-Q=f1a>g$WAGk>w_@g#zVvwZhkGwh;GYc0A-4=ZM>9I(2f*alx7h)V zSK=hez>ZUl1KU1yUIbW#E&l|xsbV3XdS$lh{wGq+q9>NPev|JL>>qyhjr(osbbx)C^ny<~kIDN1(eL9xLjO5jYm_fFh_6A2xzWK-+1NTu_Dc%4FK$tI1oR4^+*Ow=% zctK8KM*=ubNgVm?U~Dc{l#G0Qsi&#=rE;2%X(qoI#xu;M#{|!+AisAS45Te5AFbLQ z(!OxH0T6>S6KdCA`!ZJ>VB5@+mMPIb5)zUxn z9Qop89#JxP>4bxAnRfvg`K`TNxYJL;p<+r+Jpks+rNQYBfR1%J(#qDh12F+yWJcId z{An!#pPJceu46oi$*SNJOk?rxbMhctwyQ^(ENtM_%oit1|BGSsVD}Q5NqQ3L)IENG zX8;Uip2aZ!88_dAtgS`7Z)j_q6+E{(+`4%9d=q8ix9M_WH!d$u%CkqYP;nn_CRuIr zn!!V+p6@M|{+V99UoozZ9M1Nd#pYWpp}9ZgKBwpk@Kbug!~-n5fYZo$5kHXWDvu~ z-Z#M@eZNk09{iOIFfeYz;BhlN!^z`HYq-v%CRSz~HThvW^xKuTh$YG3NjX0}1RK{% z$*V)^9*KHj3hYS91O^MA4vs;LUr#h!=@Vk(nN+(VZrO-CF~~jF=8Ui%TCDs`O*ypwM-JGJ?BL6#%_Uj}Y6YfGUv}pz zKd+C>Aj!FZ4<9)K@5ul#5~dQFSF<`2v=21Iz_wp2Sut((%}R55aniXC1B2rep1Jt} zFxlGtaI4!3Q~?v#_>?~qFz%VncJ7%ciF;=A@tfb?k{1Gg9{&lT<{^9T9g#PxVpzul zAb?@Ns)4$*%ZrneJhl%v6M5!7d>K^s4->6hl4iXj81p<0HwT^qvxdOQ?GKx) z`4tV6Goo=-mMVe^BPYTemQ3O&gEDnz!5sb{X?M?a-IA^K{j{*1BMu_n4<{f)qzI5& zEhGrYknFe*2{A7Tfi1um83Iz=woJgcXb>NSy~W3{ljGmL{?Bvm(R1BYCv472&+w5X8h`DitUYDg?io^Q<)fY4RzK}z%Ih0V9cd(*bRJrH1@QEtdAa;i z_;sY`MssDQ{(K9=*3si&lYaBR#Y;ReS{V5*iDRN3pz}csljjRRZDAr=If%Mmianm& z%^pvfZULUO3|Z=U2v+ZM73R@WNxk>7^R1{eH{H#|!CdF_=1cbU)YO!^x#IWSqgwbN z5&bkNZ+e)u&F=xc5vhXKn>ml%9!GSoy$rfcd;EKz=^P=guy@ z3~e)K0)I(1QuPv&R7sr7pTL%Rkk?r$$MoA5;}dnJ-o9PHuZ5ubldmMUcgjA7m5Jvr zKnL*lh^p*!W|U7%Lx%dy-w{mk`RQo=hVtyn5gQ)%JnE#aXN zosx{l1Gs!;Au3gfc`$`k!4Pgdy+08~xFcsC_wV(Ptj8t`M@tkp70(^y z!#sM=!1e|72|}K3&@v~-wkLsSFy%Z<7%ZW^2D0Our{;V4=yR-mw&grpVYkl!w&j+K zx}nOU-R+Y;eumu6^FpclL#PKFr|~U7wq-iG`~iHtUWlso$pexG5>y3)44Jf51ArE5YUuaF3(_+mACUfU;K3VeZujyIJpbE~98xrDi^ux9mGF zEq_X30z7c9D#NJr&4T4j;eYy1|4GvBUrkE34Bhy77{6Zci}=Sju*FLlfZh0E<^VzJ zx97{B)2~%wI*}X*UwaE_OH`WcB)MrCJl@}W2du(y+W{ancaZ;vD9ei=b=PZq;vhTs z-M=$&izy#}iR2t#^dJ0=0TLF?0Z|zDZ>1-!=OF8gPu72Z)vJ%%+beXJ1jf8AhKkyA zpObiAUi|1rmof}F<}mOGd30BQ+ickRUY9N{y*j*8u$%9W)1(F#ZiWch+{eRz$p7;- zoa3Iu?A6Pko9zdcHccvq$1}po+O(J(FM>a3&lEg+SFI;>kT@Al3y+fY3a#rUx&eZ2bLEpK zle<-&S}pBT1NdBf{94*2#gAMrjs#&Q3CTip-)m3g^1}~5P>Q=vom zl3}?Ug6;k-bu~vh;BnEv(SQ7gQFHQ5xG@W_Rz6?@yEm8JPML3Rs9^EWWc4Ba4a>c7 zdU#!?rbP3qT#|iVr)^=&<Tq}LpefZwbYJ{0|I7buSu~RBPyRgeTGG3p&FrjfJq2=R*Q*QN z>2H6@c*;mYN4%RS+?cx!oDH38_1=8tI8SY>B6fAur3 z%8RmmU-ZxCuc75mS-(05%80q!tyH1=<&O2r!otb7_T=MJT{$@tMXIHW!SF$$47&+UV5wdMdM`hwhPPwcJ4$E(OWm_1YFa(EG z&m*ZA`c7%&^ua3s+kf*I9p;1mbJ!jPE@ADY_*QqGnPhn(QLOX_@u z`m{(&9h-Zw^A)b+JqAJ(f+NvirOaI6b9kHWFsKy|{=i!}!P9hPp3IG+*$Rz@u(`hc z{}2D+{~B!Nqw$~HHgp0kNmb0-05#(=EEgE5)z3BKxtb4vPhI6+?V0NcZ) z4lJqqz_E1_KAkh2!+2^vFw9jQ71fAt5_-I zJx$D}b}r+;EetD-!*VQBp@MS0oidCTX6ErLng8ei`M;O7J^GOd6W|~Hhgz7+qqWCS zM&^MfWzJ#zi~h{xb(lvtd%1M(meO6OPR9nU_~j)R2f<|QPIeC$`sRwOKe8yh$deUy zbDN#W=Ksrg{HW&A%wx!bWU!D4LOd6rod4fS>x21{Ts#Iz9}TX!ah1jTWa zl83KED5purKayd_BlT0+UN-YPl9p+CU$9lru>(LM14hzM3@^E`EA%KFZ;pu zVs)=QEIs5>npM#n26hVNUQ!``(`V$I)Th6x%HJR8y)>Tv{~?~jVEv;Sjh;jWcvE3? zS%khYE4)ee9mF6smGG@-Kdvv>AN|oExnu!)03=ItBnvCgqKd+bbh14K6@?bRT1xNb zGA(ZaEv!~PS-3)u-)!2Ft{_$8f`{_Azx}OOlnC=DTbjS|H~xmO@}wjr2OeJ3v8)Mh zrQI5XT_L#&5T{b=VGtWYm{n|1??5ay;f9a<_eJny311}nk*dQwfFup;q6yYG>0k0M zw6n?IG#_g63a98zrFo>9MzmTM(f~kl4Npk+UtZP#GBi*DULgZ?OHw-gTbloGOtv1d zI%!+R)XV7teIf!?|60q7BuAE|im|G7mX9KYt#<0if4?!kZ!ZJa%B9{el+-MJWg z+{8IN!JiN=-|k9EnvsY)`rF~mhkti0zzQwf!jdjenwzTr7~!P%iJN;XrBtK@Ca>r( z+!V<|H~w|(QHSAIg@Bb?gx{!&eS~Y|$zCezPckozydH&Ty{O|y`*R(y^2+eJ`ZE0* z0L*{Fqx>_soo77FQ0)Gl-ev`|eu7rz!9NUPd4-|T9n^eonsZpsagP&W)uWwy&JS#3`h?%H_rO{zJ9y_${AnVsrg2w6}!4iFF;& zZBYHvxB)%KZ)!X;^#QA7!#Qoy4`&6)(M=qbpq`X@x4%&9$*K|PEcP(d!DNSk}cOI2>$%K)9VH>dBJ}1-oK@nDx=#_e8aztPSy2iwsXm~Ubkv^f?GArn(J7)zbGFqt<-T6 zYQQPA7%o}1c}OPgC@Cew-M=f_0hR=I>*oMClSiH=$AqG_OJ!H8w$YzsXXugChYRlg zt7}oJQ})E1`;iFsWP}Fc!tx?$Nj#4Tjz%0yqABaT>&d$73FE&dpyt#O8VRuD)|_;&4nLmMtnP5-1!VxvsP!{j}b8$Y&z1aj27&F{a^n#13ll$FsJrs;-CM`N-W>knCl}pwtB+h4Izgl zT>t$$^pdm#>nbxGm3AQ6El-G(&|!PEij>l*+$_)KhZ(DD`_1JFkON_|L6bwHJPn`rsqXbEsqUJ$rVC=rkS7Pp)dFR6s;qjpQYYzCqwJj zQVBZJx@2{vA8qo=A8b_lP)Mnr+!*jqO_V9aNNOjuT01FqYxzi@w>^{;2i3 zaa$Ob{CyHw*42m3R9=c@ITET=|NVdeKh(nT=e96eA-PQot3~I(QQqX02IRaYIChS} z>t?Tz!>q1)f{c?O6`}gG`E$ic875m{&1$wKDZ%iUpGNI}S$|wb>p!eUOCD{QrNWd--)9y=y2kO(5bQ7grN5M+u>A-y z3_q3k>cfWi{ZyqoQ#-RWDBKrvD48h!iZb}&4lrW+@*vf+5 zy7z3Qet8`P0LKjHPZ~*4MUrx%=3{tBC1W2YVb|9wFc0q($|y~;&?)q_S+c?hE=Pg} zefwf0DQ(IO6Es!hZxKj6QTx?QS<@X*l09}21q&3wIGinK`2wTE~_aMDAl4GP;HYL6MaVm5+(rHveH<4 zZuu%=R^>Pb!pJ3MOZjKIe=)?PVjIbHBFjiBWf(9OTj;1(Odp;D^C2F9>i-Bc7tS`p zq@LbeFq;s2-b?C$-@bSnJIfwCWtaKnn+y4-@~Xx<{>CcT|F0SU$)9+-N9bXX1g2;X zfR#&?Fe?}x+*@bTGpu}KnC*lyk0*S2Nu558(Dd14fIBq+d{Yt5wtO=&OWW$ED7_=S#|@LnZC6Z5;~-B6AWRud`04%2 zYW(pqto$G5qH!j8orU0FwNC%CCMfe7fBd$h*Wb!S(`Tn)wkC|!nVg>ktEDiegwoHe za*67rbYA6ScjR&p$%;n4k*^)2%r+sbFw>_L(#KDbI+Mu{!jEM7vPGCew)gsCXkJ6ANr7!h3_Sk) zxl>(|OqZC>oBx658wMIP)DIGRm`(Xf)f-n0GF%9@{w-yNoL8ce7h-TsQ)9g-gRudg6}|Js>>(6pDeqM2X+v${!AeV{LXn@$49FQcNugtGqd z;M+ZtJOINTlK#qH`76Kv^{?&l%^yh~H<}AC*pUIE6>=bwG^Jj0dPA@tH1sB;t9ay( z(s@09F=r@Tw=)eu%wli2vhQ%SLgko#{6#Yq8qPF{59~%;FGW3M zQeI_E{^c)!IeCBTYp9L~*rJYa-0RbpuaN(eP%+e}C1>u^@2m74I-YA~B)s>p{yz;X zM-AyzE-5=N6ZA6U{`oS zJ}{{0Z$CWS^cFa?(vip{s>m&zjOU)LRC)zI{)XgH3G<&2Pn`GqmNdY!rb(Ibr?&qL zA)B|%M2-{cP35HAb0!mya>bkMbp%P*2mYs7r$}m-zFShNTryvnKwn0}m4l94%6x-# z45lMJ*%zfY#R^>l;4+p>{I!p7@k;#xQ0xco|l1I)0A8@ke;gk>3_3S^$?`$V#ZE zr#G@(VB)7)3se1%zYxZzpQgef*TR&F)}e(F?dT=T63U&4E0qRDG>q`k%?=ME z$>u`a_tSj&@}+(4CB-1g#s!(TaFt}opL+iW6PC-n@XG%ezf)4#bXJ`PP{Qa+U3Bd9GheDx9bn4AqI&1QNQ;oe zU!g*zd6=gu)47!4!cxt`Omi;)Ien{nkyM+9CoJEaR}X_U?_uWS7j+iq>$kM1p6y{U z{_?_B2S`-6MtJ!}cjyOCzU9BtAY}Wl$?!6Vabpbp@E^ES$ZDqm@1UH3{Ba62SrIHd zFL&?RO(xjUV{&0gucS~$8>|hv+L81_irPG7FO0@fAHCck{;wKrht9+8G7^@Tg^KuZ zF6CQjsyC$YE`v>(aQ%NI#k}pGs)tk4nbQ(^HaMNFGaKz#+i;mMitda`T6Drp z2rBlClEEDJHmqPNf2Mpg9M#NeR-R)>5W4%k;bbIWMo)Wv`IGCRM!V}Mdij?r6Pz6> z$x9^z_*l&})&|JKOrTU5ih9U-a?fP=*j8uKzxzKt;ZZ*WUjVsdo|MZs^^YxsU1Jt1 zRDSyhTO)zF{Hy$TwJDxkb-h9CdF<9U7IklKm#+Co6RaC{KD-j0hupB)N$DjY{rz|>?D-3^?;jicKu&cTE z*D?i2CfE|C^)3tbM4yj*`N_I6Dwy`!O{9XYa3I~+TK?Vm@f0F=SVLW2-GG!2{%d6d zuGEy*|H&Anez1RQ76{ksQ~rUk|JQ1Ou}$M0@cdB%?yt*F@2@r72tFX@t`WWbxBkSi z5V>QkCiI(rx?W8NZufec9;r)ZJ~`n<|Fzj9FLe$iOb?T0g6OCI$0oc2dCYN+`O^B1 z&GeoW_ua&8HvuqN^P>N7J7MH^;Nh*iv0Vp%8xs7U{=?6lFi!eUFVLKM@b3a4QBR^0 zI%bmKEN(88!%U#;^fTE67C^QMd2|E9QMTa5W?5;j5 z5499B(+N;_{c>LUcGu~nz5b_J%_$ng<)Lrp zg)se3mCrJ=PHZ8$sD{z4-8}npiAv~5Ss3#Tjnz%iNVPWUjx%|!+!b3gq z`3W}v@9D2vCfLxurqxuJ-M-@=!__QP)Dx zH#@22)aF}t`0l&!XdD#_AvV#Pal+*%Unh~TB-!4i57O5$!Xd}`4e|&2;di>9e&BJ+ ziyYek+g?ULlkvBUqFf9Omfg$=kMbG+1wZ8R$8$|4&zUsMTP}5kol)R<692uFd!&wG z{F@QQ>6L;3mPKsBD=c-w!w|Tn1iJ%8KPX;b+_|ss@RKsI9_b76HJUQ!+ZO3a&tzvE zxQ=vkAtZ)qCtSdlcNQx>nzfU+{#>i;TYajfmiwYZ&i@H8tE=RBvKA&PVM2gHEzFHF zhbhx>s?3v15^7;K73PC!>1Qi?tGyOxUJWy0w$-S2z1%6sFg(qDKx|wYVAoe~)|1hD zV>aDdeUE?B-AL+uvU=oFj&(jIHQk*#@c79*e7KhnaYidlDCx>HGp8RQ;igVA;lFz% z&mNMey3@12qNgcyKcw*4wvDu*{_;*?wsXFC7R>RzqO|x9Wm-W44 zd!a;XNx}ML2ieh1Y`Q%}mn7RuxR6%kHzG;}0L3&~`I0glbYk zRyVXrxHkjh&%NZ3X>Qp487F-(ZiErFM=vFwgu!TfkDfs36nqUIX0U6rQ?UIU|7{n% zA?|Xwgsw&CkLj{)C!Ig(@_lWzKEa`v_T|eLvK$@o#+JH+_3u9jmkwJ3pONaD$=C>da1rC3r{Vf0SC+n6kDQz{{hkdlU``HKb~Cj4bcwi=hMu=;uVvnnjFU|nBw;L%=T zwlhx%BaF}kW82jgg$KFos1~`1UU}Zu;AK>%BS~0ZMS+*;kd!ukuS(N<J}9TqJa<(piy!Lht^CUl zlA*aFe<$m6!1QIRYvLdSxK(|0!otkPGhqhKu{ezt_Zpl`OM-VyCM>EAT+PQ|K&9GO z^I?qVSM%eiyuzpVZ~R~bm-W?ZgoHIFngT z7-m*qzI<8dKU-j_J2J^z$}wm>C-2?mY~#_=4!==unq@JDJS;lQci(+CJwbQWUSp|b zM=*{h6ppZHu*X{zMj0=HA5T-%)UyxMYXr!)4tQj5ucDr?fvER0yzm{ZMb_;flB1;* z`t3=|hhAq4ueg;Edlv6b#2@TtG+B<-e;+HeGEHwX$l8GN3}d{YdUzqyHvF6EKllg#ptTb)BZYnM zba@dZ6}1ddeTy^Y*C@ z>H`nk%^85_1R;#hSn>v{8GfAJ@7~Mtr;jH?t&`4A@;X1j+)zG!jYH?BvSJSD02o!# z<(u$A{a8Om^QR$m=7pIThS>&%QKi2a8ZM>3JeEeR&eHSW3pDGm8Xj(-DmALPv5ck} z^D?~xIe>;GTcPIyy(5x@Qr+6yBv<{R2SDQZu=&L=?p!}*uu>xw&NkYGF$9JoNTLit zC~2CWbVYBNVYYY()1=gfdlxFbmZa}`RX6Z#bO~T>=HUjVqwMnMYLe+o0&|e*$6pf7 z2k3Y2U*H&oU%q^Crvs3$KETyaCTJv!g4IFIcFNc)mgH`_Wsz_Jvn7-&oLuV6aSl(m z)w0Hi(%iQ<*TatF;gX?vTZWeK_ z*%5_lLVygp&gX11DfGfI40(TJk$&I=$_Zp94w?=!<)7*PWf+gpQ8c+w{h|C=IN4D) zgBE?H<`yN`12E)I2|v%D)#comb+7`;w;%3(_SnXqBZ7z}kV6eX;=p~0z^yKL<&xLHj8?z``@{h4s!hyUf9 z5@s+|O%seWhwQd*Zg8`uUPDXAQnMDPgkkr3|j^XijTKV24|eXxVoeu`0*`e1uxt#_i#y&?uI^tfruRkoi# zZXSe17vk@(jN$hQvwb-BFoXg4dCbFP(X3{R>OLCU3ll#Ru>2uU8P)V-R`M!v^GBub z#&1P!$f)K+`bF<^d?phHOY`vRk@|>^E)SCQ%SCkZ zs7zaE-1a~>RZ;s4xj)(gwi92$j{m`&iJNQN&BmVqe=o;U^CvdSclS~T5CU|8(sY5w z_AcBJjtlhQU%p?ye3={xQmY@?vh%({5%@y3JyE}Xb8Y9h*anTnEKQ-UmR6EVy^sXUB7 zJs8_MKXRvB=cl~t{CGd?)2D4DH!R<5=|6SjE&_cDVE9uH;s^c_%@axv)L-qk4Q2;h z16B9bJiN|N{`u7Sfx*Zw-wf}@mnq+c@#ok8wTp87LM zCT+a&pNSRm#}^m9L^!s6v16JEj}w3Be=7H^J}sXyBs`unkJLr48|3}N7JyUREX0N7 zTOCxj^wSRN{Lj^8cZJJ&^~t50RM`Ohzx}uWCW+qh=t%Rk93?%d@>2u;sh@%GtsTSl z|3P@8U^mD2cJhLg$G>)xe(mJNByTpA;k+v8(=f~)SU+P0bN&ArIu_e%D{uuguDkMn)JW6CBe_2rufG3`w4* zXsKvhkT4hHd1lp4*VaBD-wBByOp@hmf~S%G-;4Xl9U`I_B3~|qj+IZ*`5+q4SKO;4 z?`J*zkE?vc#Q!*p5iV<*of?_9lBRjcS#^$u^W^eWJ|mIj?uu%*8Fr@kg9APbV0|L+ za=-a96uk>VQ3*{C?SI?MR@+^LUjt!|z!oh%9xE$eG3JBzqJS>JJz| zsa2KuhYt{@q?i|u!AtJ0xa*$AjulZ8A6ji1?fB+T1LRnqkSVX8Tz0`qnpZ}u>4iS# zuz#=&FixqHKiFR_H22}ueI2YY81t9K$AZGY#h z@{=LW&$>hHcc?0^@=FRp$S_(jf4J$qdh#Ft<9}>AGFW-}`lODmXJNgclyc9u(9EDR zH05o_WkYCdoW34lgEOk!xOg%Kd%k|Y=BxA7{Mlm5f&4ElHT~p!rVn#SO{lu$cJo^b6bqZl=49ydNcOzDuS*|+A(M5EE;CUar7!goTN zt9(Q;mpTDSN1AW34`#=rxBd)CNG=>tLgp{X*Gy?d+X7Z<^@kik! jMIEf-OzF#e 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 N<7+qdD|PK+{u|myHyQu{ literal 0 HcmV?d00001 diff --git a/examples/research_cod/exp_plan_final/bench/eval_alchemy_easy.yaml b/examples/research_cod/exp_plan_final/bench/eval_alchemy_easy.yaml new file mode 100644 index 00000000000..aa51865ead7 --- /dev/null +++ b/examples/research_cod/exp_plan_final/bench/eval_alchemy_easy.yaml @@ -0,0 +1,89 @@ +# OOD eval: Alchemy-Random EASY +# Data prep: +# python examples/research_cod/get_alchemy_data.py --local_dir examples/research_cod/data/alchemy_random --train_size 50000 --test_size 4000 --seed 42 +project: "trinity-cod-final" +group: ${oc.env:EVAL_GROUP,frozen_lake_obscure} +name: ${oc.env:EVAL_NAME,qwen3-8b-flobs} +ray_namespace: "ood-alchemy-easy-4k-${oc.env:EVAL_GROUP,frozen_lake_obscure}-seed${oc.env:EVAL_SEED,1}" +mode: bench +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + eval_task_pack_size: 8 + cod_workflow_args: + activated_cod_methods: ["iterative_hint_e2e"] + hint_example: false + reuse_workflow_instance: false + max_response_tokens_restraint: 1500 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name}-ood-alchemy-easy-seed${oc.env:EVAL_SEED,1} + inject_pack_seed: true + pad_tasks_to_full_pack: true +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 30000 + max_response_tokens: 2000 + max_model_len: 32000 +cluster: + node_num: 1 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + explorer_input: + eval_tasksets: + - name: alchemy_random + storage_type: file + path: "examples/research_cod/data/alchemy_random" + split: test + workflow_args: + context_compression_mode: "keep_all" + length_penalty_coef: 0.1 + len_full_penalty: 2000000 + len_zero_penalty: 1000000 + max_rounds: 8 + material_mode: "unlimited" + scope: "per_pack" + show_recipes: false + show_elements: false + show_tiers: true + min_num_tiers: 3 + max_num_tiers: 4 + min_base_elements: 3 + max_base_elements: 4 + tier_shrink_min: 0.4 + tier_shrink_max: 0.6 + min_recipes_per_element: 1 + max_recipes_per_element: 1 + max_tier_gap: 1 + min_cross_tier_prob: 0.0 + max_cross_tier_prob: 0.1 + min_noise_node_ratio: 0.0 + max_noise_node_ratio: 0.0 + noise_chain_depth: 2 + rollout_args: + temperature: 1.0 + data_selector: + selector_type: shuffle + seed: ${oc.env:EVAL_SEED,1} + default_workflow_type: 'cod_random_alchemy_workflow' +explorer: + eval_on_startup: true + bench_on_latest_checkpoint: false # eval EVERY saved ckpt -> curve over ckpt step + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: ${oc.env:EVAL_SEED,1} + enable_openai_api: true +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/bench/eval_alchemy_hard.yaml b/examples/research_cod/exp_plan_final/bench/eval_alchemy_hard.yaml new file mode 100644 index 00000000000..cc48e928fcd --- /dev/null +++ b/examples/research_cod/exp_plan_final/bench/eval_alchemy_hard.yaml @@ -0,0 +1,89 @@ +# OOD eval: Alchemy-Random HARD +# Data prep: +# python examples/research_cod/get_alchemy_data.py --local_dir examples/research_cod/data/alchemy_random --train_size 50000 --test_size 4000 --seed 42 +project: "trinity-cod-final" +group: ${oc.env:EVAL_GROUP,mixed_flobs_alchran} +name: ${oc.env:EVAL_NAME,qwen3-8b-flobs} +ray_namespace: "ood-alchemy-hard-4k-${oc.env:EVAL_GROUP,mixed_flobs_alchran}-seed${oc.env:EVAL_SEED,1}" +mode: bench +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + eval_task_pack_size: 8 + cod_workflow_args: + activated_cod_methods: ["iterative_hint_e2e"] + hint_example: false + reuse_workflow_instance: false + max_response_tokens_restraint: 1500 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name}-ood-alchemy-hard-seed${oc.env:EVAL_SEED,1} + inject_pack_seed: true + pad_tasks_to_full_pack: true +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 30000 + max_response_tokens: 2000 + max_model_len: 32000 +cluster: + node_num: 1 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + explorer_input: + eval_tasksets: + - name: alchemy_random + storage_type: file + path: "examples/research_cod/data/alchemy_random" + split: test + workflow_args: + context_compression_mode: "keep_all" + length_penalty_coef: 0.1 + len_full_penalty: 2000000 + len_zero_penalty: 1000000 + max_rounds: 15 + material_mode: "limited" + scope: "per_pack" + show_recipes: false + show_elements: false + show_tiers: true + min_num_tiers: 3 + max_num_tiers: 4 + min_base_elements: 4 + max_base_elements: 6 + tier_shrink_min: 0.4 + tier_shrink_max: 0.8 + min_recipes_per_element: 1 + max_recipes_per_element: 2 + max_tier_gap: 1 + min_cross_tier_prob: 0.0 + max_cross_tier_prob: 0.05 + min_noise_node_ratio: 0.0 + max_noise_node_ratio: 0.3 + noise_chain_depth: 2 + rollout_args: + temperature: 1.0 + data_selector: + selector_type: shuffle + seed: ${oc.env:EVAL_SEED,1} + default_workflow_type: 'cod_random_alchemy_workflow' +explorer: + eval_on_startup: true + bench_on_latest_checkpoint: false # eval EVERY saved ckpt -> curve over ckpt step + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: ${oc.env:EVAL_SEED,1} + enable_openai_api: true +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/bench/eval_frozenlake_obscure_hard.yaml b/examples/research_cod/exp_plan_final/bench/eval_frozenlake_obscure_hard.yaml new file mode 100644 index 00000000000..fa240f4d265 --- /dev/null +++ b/examples/research_cod/exp_plan_final/bench/eval_frozenlake_obscure_hard.yaml @@ -0,0 +1,73 @@ +# OOD eval: FrozenLake-Obscure HARD (map "6767", env/agent steps 8/10) +# python examples/research_cod/get_frozen_lake_data.py --local_dir examples/research_cod/data/frozen_lake_6767 --train_size 50000 --test_size 4000 --map_min_size 6 --map_max_size 7 --tile_min_prob 0.6 --tile_max_prob 0.7 +project: "trinity-cod-final" +group: ${oc.env:EVAL_GROUP,frozen_lake_obscure} +name: ${oc.env:EVAL_NAME,qwen3-8b-flobs} +ray_namespace: "ood-flobs-hard-4k-${oc.env:EVAL_GROUP,frozen_lake_obscure}-seed${oc.env:EVAL_SEED,1}" +mode: bench +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + eval_task_pack_size: 8 + cod_workflow_args: + activated_cod_methods: ["iterative_hint_e2e"] + hint_example: false + reuse_workflow_instance: false + max_response_tokens_restraint: 1500 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name}-ood-flobs-hard-seed${oc.env:EVAL_SEED,1} + inject_pack_seed: true + pad_tasks_to_full_pack: true +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 30000 + max_response_tokens: 2000 + max_model_len: 32000 +cluster: + node_num: 1 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + explorer_input: + eval_tasksets: + - name: frozen_lake_obscure_eval_hard + storage_type: file + path: "examples/research_cod/data/frozen_lake_6767" + split: test + format: + prompt_key: 'task_desc' + workflow_args: + context_compression_mode: "keep_all" + length_penalty_coef: 0.1 + len_full_penalty: 2000000 + len_zero_penalty: 1000000 + env_max_steps: 8 + agent_max_steps: 10 + mapping_mode: "per_pack" + rollout_args: + temperature: 1.0 + data_selector: + selector_type: shuffle + seed: ${oc.env:EVAL_SEED,1} + default_workflow_type: 'cod_frozenlake_obscure_workflow' +explorer: + eval_on_startup: true + bench_on_latest_checkpoint: false # eval EVERY saved ckpt -> curve over ckpt step + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: ${oc.env:EVAL_SEED,1} + enable_openai_api: true +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/bench/eval_mixed_opd_steps_0to60_all_domains.yaml b/examples/research_cod/exp_plan_final/bench/eval_mixed_opd_steps_0to60_all_domains.yaml new file mode 100644 index 00000000000..75aac2ad72f --- /dev/null +++ b/examples/research_cod/exp_plan_final/bench/eval_mixed_opd_steps_0to60_all_domains.yaml @@ -0,0 +1,156 @@ +# Evaluate the mixed-domain OPD student at steps 0, 5, ..., 60 on PDE, +# optimal control, and grid navigation in one benchmark job. +# Data prep: +# python examples/research_cod/get_pde_discovery_data.py --local_dir examples/research_cod/data/pde_discovery_eval_hard_stratified_4000_disjoint_testonly --train_size 1 --test_size 4000 --seed 20260902 --eval_pack_size 8 --eval_template_count 25 --eval_ground_truth_family physical_full_eval_4000.json --test_only +# python examples/research_cod/get_optimal_control_data.py --local_dir examples/research_cod/data/optimal_control --train_size 50000 --test_size 4000 --difficulty hard --train_seed 42 --test_seed 2024 +# python examples/research_cod/get_grid_navigation_data.py --local_dir examples/research_cod/data/grid_navigation --train_size 50000 --test_size 4000 --seed 42 +# +# Trinity bench evaluates every global_step_* directory it can see and does not +# provide a checkpoint-step filter. The checkpoint directory below is therefore +# an eval facade that must expose only global_step_{5,10,...,60}. Step 0 is the +# base model and is evaluated through explorer.eval_on_startup. +# +# The facade may contain symlinks to the model.safetensors files in the original +# training run, leaving that run untouched: +# checkpoints/trinity-cod/mixed_multi_teacher_opd/ +# / +# +# Run this single config as an 8-node x 4-GPU DLC job. Each target domain is a +# separate eval taskset, batch, and metric namespace. + +project: ${oc.env:EVAL_PROJECT,trinity-cod} +group: ${oc.env:EVAL_GROUP,mixed_multi_teacher_opd} +name: ${oc.env:EVAL_NAME,mixed-opd-eval-5to60} +ray_namespace: "mixed-opd-0to60-all-domains-seed${oc.env:EVAL_SEED,1}" +mode: bench +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: true + +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + packing_strategy: cod + cod_workflow_args: + activated_cod_methods: ["iterative_hint_e2e"] + hint_example: false + reuse_workflow_instance: false + context_compression_mode: "keep_all" + max_response_tokens_restraint: 4000 + hint_penalty_coef: 0.1 + cod_log_interval: 1 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name}-seed${oc.env:EVAL_SEED,1} + inject_pack_seed: true + stable_pack_seed: true + pad_tasks_to_full_pack: true + +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 27000 + max_response_tokens: 4000 + max_model_len: 32000 + +cluster: + node_num: 8 + gpu_per_node: 4 + +buffer: + total_epochs: 1 + batch_size: 64 + explorer_input: + eval_tasksets: + - name: pde_discovery + storage_type: file + path: ${oc.env:PDE_EVAL_DATA_DIR,examples/research_cod/data/pde_discovery_eval_hard_stratified_4000_disjoint_testonly} + split: test + format: + prompt_key: "task_desc" + response_key: "answer" + workflow_args: + update_context_impl: "pde_update_context" + context_compression_mode: "keep_all" + max_steps: 6 + point_budget: 6 + min_reaction_terms: 1 + max_reaction_terms: 3 + ground_truth_family: "physical_full_eval_4000.json" + pde_state_abs_limit: 3.0 + initial_condition_shape: + mode_count_range: [1, 2] + noise_level: 0.002 + kappa_threshold: 100.0 + dump_trajectories: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + repeat_times: 1 + default_workflow_type: "trinity.common.workflows.connect_the_dots.pde_discovery.workflow.CoDPDEDiscoveryWorkflow" + + - name: optimal_control_1d + storage_type: file + path: ${oc.env:CONTROL_1D_EVAL_DATA_DIR,examples/research_cod/data/optimal_control} + split: test + format: + prompt_key: "task_desc" + workflow_args: + update_context_impl: "update_context" + context_compression_mode: "keep_all" + include_previous_control_policy: true + a_env_range: [0.55, 1.20] + b_env_range: [-1.5, 1.5] + min_abs_b_env: 0.35 + enable_process_noise: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + repeat_times: 1 + default_workflow_type: "cod_optimalcontrol_workflow" + + - name: grid_navigation + storage_type: file + path: ${oc.env:GRID_NAVIGATION_EVAL_DATA_DIR,examples/research_cod/data/grid_navigation} + split: test + workflow_args: + update_context_impl: "update_context" + context_compression_mode: "keep_all" + length_penalty_coef: 0.0 + grid_min_size: 10 + grid_max_size: 14 + landscape_num_components: 5 + landscape_min_scale: 0.15 + landscape_max_scale: 0.45 + min_rounds: 3 + max_rounds: 5 + reveal_radius: 1 + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + repeat_times: 1 + default_workflow_type: "cod_grid_navigation_workflow" + +explorer: + name: mixed_opd_0to60_all_domains_seed${oc.env:EVAL_SEED,1} + eval_on_startup: true + bench_on_latest_checkpoint: false + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 32 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: ${oc.env:EVAL_SEED,1} + +synchronizer: + sync_method: "checkpoint" + sync_timeout: 72000 + +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml b/examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml new file mode 100644 index 00000000000..3d357ba0e14 --- /dev/null +++ b/examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml @@ -0,0 +1,168 @@ +# Evaluate every checkpoint from one source-domain training run on all three CoD +# domains. Submit this same config as one independent DLC job per source domain; +# the jobs may run in parallel without a shell launcher. Each job evaluates the +# base model first and then every global_step_* checkpoint in order. +# +# Generate the three fixed 4000-task eval datasets once before submitting jobs: +# python examples/research_cod/get_pde_discovery_data.py \ +# --local_dir examples/research_cod/data/pde_discovery_eval_hard_stratified_4000_disjoint_testonly \ +# --train_size 1 --test_size 4000 --seed 20260902 \ +# --eval_pack_size 8 --eval_template_count 25 \ +# --eval_ground_truth_family physical_full_eval_4000.json --test_only +# python examples/research_cod/get_optimal_control_data.py \ +# --local_dir examples/research_cod/data/optimal_control \ +# --train_size 50000 --test_size 4000 --difficulty hard \ +# --train_seed 42 --test_seed 2024 +# python examples/research_cod/get_grid_navigation_data.py \ +# --local_dir examples/research_cod/data/grid_navigation \ +# --train_size 50000 --test_size 4000 --seed 42 +# The generated parquet files are ignored by Git. On DLC, either prepare them in +# the checkout or point PDE_EVAL_DATA_DIR, CONTROL_1D_EVAL_DATA_DIR, and +# GRID_NAVIGATION_EVAL_DATA_DIR at fixed shared copies. +# +# Use EVAL_SEED=1,2,3 for independent rollout seeds. PDE selects its specialized +# update-context workflow through the taskset's domain-specific workflow_args. +# The defaults select the converted optimal-control bf16 facade. Override the +# EVAL_* variables for PDE or grid-navigation. Every source directory must be a +# safe facade in which each global_step_*/actor contains model.safetensors; do +# not point this benchmark at multi-shard huggingface/ directories. +# Run with: +# EVAL_SEED=1 \ +# trinity run \ +# --config examples/research_cod/exp_plan_final/bench/eval_source_checkpoints_all_domains.yaml \ +# --dlc + +project: ${oc.env:EVAL_PROJECT,trinity-cod} +group: ${oc.env:EVAL_GROUP,optimal_control} +name: ${oc.env:EVAL_NAME,qwen3.6-27b-optimal-control-cod-bf16-100to500} +ray_namespace: "domain-transfer-${oc.env:EVAL_TRAIN_DOMAIN,control}-seed${oc.env:EVAL_SEED,1}" +mode: bench +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: true + +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + packing_strategy: cod + cod_workflow_args: + activated_cod_methods: ["iterative_hint_e2e"] + hint_example: false + reuse_workflow_instance: false + context_compression_mode: "keep_all" + max_response_tokens_restraint: 4000 + hint_penalty_coef: 0.1 + cod_log_interval: 1 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name}-domain-transfer-seed${oc.env:EVAL_SEED,1} + inject_pack_seed: true + stable_pack_seed: true + pad_tasks_to_full_pack: true + +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 27000 + max_response_tokens: 4000 + max_model_len: 32000 + +cluster: + node_num: 6 + gpu_per_node: 4 + +buffer: + total_epochs: 1 + batch_size: 64 + explorer_input: + eval_tasksets: + - name: pde_discovery + storage_type: file + path: ${oc.env:PDE_EVAL_DATA_DIR,examples/research_cod/data/pde_discovery_eval_hard_stratified_4000_disjoint_testonly} + split: test + format: + prompt_key: "task_desc" + response_key: "answer" + workflow_args: + update_context_impl: "pde_update_context" + context_compression_mode: "keep_all" + max_steps: 6 + point_budget: 6 + min_reaction_terms: 1 + max_reaction_terms: 3 + ground_truth_family: "physical_full_eval_4000.json" + pde_state_abs_limit: 3.0 + initial_condition_shape: + mode_count_range: [1, 2] + noise_level: 0.002 + kappa_threshold: 100.0 + dump_trajectories: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + repeat_times: 1 + default_workflow_type: "trinity.common.workflows.connect_the_dots.pde_discovery.workflow.CoDPDEDiscoveryWorkflow" + + - name: optimal_control_1d + storage_type: file + path: ${oc.env:CONTROL_1D_EVAL_DATA_DIR,examples/research_cod/data/optimal_control} + split: test + format: + prompt_key: "task_desc" + workflow_args: + context_compression_mode: "keep_all" + include_previous_control_policy: true + a_env_range: [0.55, 1.20] + b_env_range: [-1.5, 1.5] + min_abs_b_env: 0.35 + enable_process_noise: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + repeat_times: 1 + default_workflow_type: "cod_optimalcontrol_workflow" + + - name: grid_navigation + storage_type: file + path: ${oc.env:GRID_NAVIGATION_EVAL_DATA_DIR,examples/research_cod/data/grid_navigation} + split: test + workflow_args: + context_compression_mode: "keep_all" + length_penalty_coef: 0.0 + grid_min_size: 10 + grid_max_size: 14 + landscape_num_components: 5 + landscape_min_scale: 0.15 + landscape_max_scale: 0.45 + min_rounds: 3 + max_rounds: 5 + reveal_radius: 1 + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + repeat_times: 1 + default_workflow_type: "cod_grid_navigation_workflow" + +explorer: + name: domain_transfer_${oc.env:EVAL_TRAIN_DOMAIN,control}_seed${oc.env:EVAL_SEED,1} + eval_on_startup: true + bench_on_latest_checkpoint: false + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 24 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: ${oc.env:EVAL_SEED,1} + +synchronizer: + sync_method: "checkpoint" + sync_timeout: 72000 + +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/bench/eval_strong_api_model_all_domains.yaml b/examples/research_cod/exp_plan_final/bench/eval_strong_api_model_all_domains.yaml new file mode 100644 index 00000000000..fc5968628e9 --- /dev/null +++ b/examples/research_cod/exp_plan_final/bench/eval_strong_api_model_all_domains.yaml @@ -0,0 +1,176 @@ +# Evaluate one stronger OpenAI-compatible API model on the fixed PDE, +# optimal-control, and grid-navigation test sets. +# Data prep: +# python examples/research_cod/get_pde_discovery_data.py --local_dir examples/research_cod/data/pde_discovery_eval_hard_stratified_4000_disjoint_testonly --train_size 1 --test_size 4000 --seed 20260902 --eval_pack_size 8 --eval_template_count 25 --eval_ground_truth_family physical_full_eval_4000.json --test_only +# python examples/research_cod/get_optimal_control_data.py --local_dir examples/research_cod/data/optimal_control --train_size 50000 --test_size 4000 --difficulty hard --train_seed 42 --test_seed 2024 +# python examples/research_cod/get_grid_navigation_data.py --local_dir examples/research_cod/data/grid_navigation --train_size 50000 --test_size 4000 --seed 42 +# +# This experiment does not commit any Trinity core changes. Before launching it, +# apply a temporary, uncommitted DSW adapter that makes ModelWrapper.chat_async +# call the OpenAI-compatible API for external models, returns model_version=0, +# tolerates base URLs ending in /v1, and reads QWEN_REASONING_EFFORT or +# QWEN_THINKING_BUDGET from the environment. The same temporary adapter makes +# Optimal Control delegate _chat to ModelWrapper.chat_async, so all three +# domains share one API path. +# +# Thinking controls differ by model: +# qwen3.8-max supports reasoning_effort=none/low/medium/xhigh; none disables +# thinking, while low/medium/xhigh select increasing inference intensity. +# qwen3.7-plus instead uses enable_thinking=true/false and a numeric +# thinking_budget; it has no native low/medium/xhigh reasoning_effort levels. +# +# Primary comparison (approximately 4096 reasoning tokens for both models): +# qwen3.8-max: reasoning_effort=low +# qwen3.7-plus: enable_thinking=true, thinking_budget=4096 +# both: max_prompt_tokens=54000, max_completion_tokens=8000 +# The larger prompt budget accommodates prior responses carried into later +# rounds/episodes and reduces truncation relative to the 27B 27000/4000 setup. +# In both runs, set preserve_thinking=false because the workflows do not retain +# reasoning_content in assistant-message history. The temporary DSW adapter +# supplies these OpenAI-compatible request parameters. +# +# Performance experiments to consider after the primary run: +# 1. token-matched comparison with the trained 27B model: response=4000; +# 2. qwen3.8-max reasoning effort: none / low / medium / xhigh; +# 3. qwen3.7-plus thinking: disabled or budgets 4096 / 16384 / 32768; +# 4. completion budget: 8000 / 16000 / 32000, guided by truncation rate. +# Budgets above 8000 require a larger max_completion_tokens value; otherwise +# the total completion cap truncates reasoning and/or leaves too little answer. + +project: ${oc.env:EVAL_PROJECT,trinity-cod} +group: ${oc.env:EVAL_GROUP,strong_api_model_all_domains} +name: ${oc.env:EVAL_NAME,qwen3.8-max-low-all-domains} +ray_namespace: "strong-api-${oc.env:EVAL_PROFILE,low}-seed${oc.env:EVAL_SEED,1}" +mode: bench +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false + +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + packing_strategy: cod + cod_workflow_args: + activated_cod_methods: ["iterative_hint_e2e"] + hint_example: false + reuse_workflow_instance: false + context_compression_mode: "keep_all" + max_response_tokens_restraint: ${oc.env:EVAL_MAX_RESPONSE_TOKENS,8000} + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name}-seed${oc.env:EVAL_SEED,1} + inject_pack_seed: true + stable_pack_seed: true + pad_tasks_to_full_pack: true + +model: + model_path: ${oc.env:TRINITY_MODEL_NAME,qwen3.8-max} + max_prompt_tokens: ${oc.env:EVAL_MAX_PROMPT_TOKENS,54000} + max_response_tokens: ${oc.env:EVAL_MAX_RESPONSE_TOKENS,8000} + max_model_len: ${oc.env:EVAL_MAX_MODEL_LEN,64000} + enable_thinking: ${oc.decode:${oc.env:QWEN_ENABLE_THINKING,true}} + external_model: + enable: true + model_name: ${oc.env:TRINITY_MODEL_NAME,qwen3.8-max} + base_url_env: OPENAI_BASE_URL + api_key_env: OPENAI_API_KEY + +cluster: + ray_address: "auto" + node_num: 1 + gpu_per_node: 0 + +buffer: + total_epochs: 1 + batch_size: 64 + explorer_input: + eval_tasksets: + - name: pde_discovery + storage_type: file + path: ${oc.env:PDE_EVAL_DATA_DIR,examples/research_cod/data/pde_discovery_eval_hard_stratified_4000_disjoint_testonly} + split: test + format: + prompt_key: "task_desc" + response_key: "answer" + workflow_args: + update_context_impl: "pde_update_context" + context_compression_mode: "keep_all" + max_steps: 6 + point_budget: 6 + min_reaction_terms: 1 + max_reaction_terms: 3 + ground_truth_family: "physical_full_eval_4000.json" + pde_state_abs_limit: 3.0 + initial_condition_shape: + mode_count_range: [1, 2] + noise_level: 0.002 + kappa_threshold: 100.0 + dump_trajectories: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + repeat_times: 1 + default_workflow_type: "trinity.common.workflows.connect_the_dots.pde_discovery.workflow.CoDPDEDiscoveryWorkflow" + + - name: optimal_control_1d + storage_type: file + path: ${oc.env:CONTROL_1D_EVAL_DATA_DIR,examples/research_cod/data/optimal_control} + split: test + format: + prompt_key: "task_desc" + workflow_args: + update_context_impl: "update_context" + context_compression_mode: "keep_all" + include_previous_control_policy: true + a_env_range: [0.55, 1.20] + b_env_range: [-1.5, 1.5] + min_abs_b_env: 0.35 + enable_process_noise: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + repeat_times: 1 + default_workflow_type: "cod_optimalcontrol_workflow" + + - name: grid_navigation + storage_type: file + path: ${oc.env:GRID_NAVIGATION_EVAL_DATA_DIR,examples/research_cod/data/grid_navigation} + split: test + workflow_args: + update_context_impl: "update_context" + context_compression_mode: "keep_all" + length_penalty_coef: 0.0 + grid_min_size: 10 + grid_max_size: 14 + landscape_num_components: 5 + landscape_min_scale: 0.15 + landscape_max_scale: 0.45 + min_rounds: 3 + max_rounds: 5 + reveal_radius: 1 + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + repeat_times: 1 + default_workflow_type: "cod_grid_navigation_workflow" + +explorer: + name: strong_api_${oc.env:EVAL_PROFILE,low}_seed${oc.env:EVAL_SEED,1} + concurrent_mode: asynchronous + eval_on_startup: true + bench_on_latest_checkpoint: false + runner_per_model: ${oc.env:EVAL_RUNNER_PER_MODEL,32} + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + engine_type: external + enable_history: false + engine_num: 1 + tensor_parallel_size: 1 + seed: ${oc.env:EVAL_SEED,1} + +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/bench/eval_terminal.yaml b/examples/research_cod/exp_plan_final/bench/eval_terminal.yaml new file mode 100644 index 00000000000..975864e9ce5 --- /dev/null +++ b/examples/research_cod/exp_plan_final/bench/eval_terminal.yaml @@ -0,0 +1,68 @@ +# OOD eval: Terminal +# Data prep: +# python examples/research_cod/get_terminal_data.py --local_dir examples/research_cod/data/terminal --train_size 50000 --test_size 4000 --seed 42 --composite_ratio 0.5 +project: "trinity-cod-final" +group: ${oc.env:EVAL_GROUP,frozen_lake_obscure} +name: ${oc.env:EVAL_NAME,qwen3-8b-flobs} +ray_namespace: "ood-terminal-4k-${oc.env:EVAL_GROUP,frozen_lake_obscure}-seed${oc.env:EVAL_SEED,1}" +mode: bench +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + eval_task_pack_size: 8 + cod_workflow_args: + activated_cod_methods: ["iterative_hint_e2e"] + hint_example: false + reuse_workflow_instance: false + max_response_tokens_restraint: 1500 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name}-ood-terminal-seed${oc.env:EVAL_SEED,1} + inject_pack_seed: true + pad_tasks_to_full_pack: true +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 30000 + max_response_tokens: 2000 + max_model_len: 32000 +cluster: + node_num: 1 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + explorer_input: + eval_tasksets: + - name: terminal + storage_type: file + path: "examples/research_cod/data/terminal" + split: test + workflow_args: + agent_max_steps: 12 + composite_ratio: 0.5 + context_compression_mode: "keep_all" + rollout_args: + temperature: 1.0 + data_selector: + selector_type: shuffle + seed: ${oc.env:EVAL_SEED,1} + default_workflow_type: 'cod_terminal_workflow' +explorer: + eval_on_startup: true + bench_on_latest_checkpoint: false # eval EVERY saved ckpt -> curve over ckpt step + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: ${oc.env:EVAL_SEED,1} + enable_openai_api: true +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/bench/run_eval.sh b/examples/research_cod/exp_plan_final/bench/run_eval.sh new file mode 100644 index 00000000000..6a3116d76b7 --- /dev/null +++ b/examples/research_cod/exp_plan_final/bench/run_eval.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# OOD-generalization eval: evaluate trained checkpoint(s) on a harder in-domain environment and on +# unseen cross-domain environments. Each job bench-evals EVERY saved checkpoint (a reward curve over +# checkpoint step). ckpt dir = /trinity-cod-final//; jobs use pack_size=8, 1 node x 8 GPU. +# +# frozen_lake_obscure ckpt: flobs (FrozenLake-hard, in-domain) + alchemy (easy) + terminal +# mixed_flobs_alchran ckpt: flobs (FrozenLake-hard, in-domain) + alchemy (hard) + terminal +# +# Usage (run from repo root; set TRINITY_CHECKPOINT_ROOT_DIR if ckpts aren't at the yaml default): +# bash .../run_eval.sh --train-tasks [--eval-tasks ] +# --train-tasks frozen_lake_obscure | mixed_flobs_alchran (one or more) +# --eval-tasks flobs | alchemy | terminal (default: all 3; comma- or space-separated) +# +# bash .../run_eval.sh --train-tasks frozen_lake_obscure +# bash .../run_eval.sh --train-tasks frozen_lake_obscure mixed_flobs_alchran --eval-tasks alchemy terminal +set -u +B="examples/research_cod/exp_plan_final/bench" +SEED="${EVAL_SEED:-1}" + +TRAIN_TASKS=() +EVAL_TASKS=() +mode="" +while [ $# -gt 0 ]; do + case "$1" in + --train-tasks|--train-task) mode=train ;; + --eval-tasks|--eval-task) mode=eval ;; + -h|--help) sed -n '2,13p' "$0"; exit 0 ;; + --*) echo "Unknown flag: $1"; exit 1 ;; + *) case "$mode" in + train) TRAIN_TASKS+=("$1") ;; + eval) EVAL_TASKS+=(${1//,/ }) ;; + *) echo "Pass --train-tasks before task names"; exit 1 ;; + esac ;; + esac + shift +done +[ ${#TRAIN_TASKS[@]} -ge 1 ] || { echo "usage: run_eval.sh --train-tasks ... [--eval-tasks ...]"; exit 1; } +[ ${#EVAL_TASKS[@]} -ge 1 ] || EVAL_TASKS=(flobs alchemy terminal) + +for t in "${TRAIN_TASKS[@]}"; do case "$t" in frozen_lake_obscure|mixed_flobs_alchran) ;; *) echo "unknown train-task: $t"; exit 1 ;; esac; done +for e in "${EVAL_TASKS[@]}"; do case "$e" in flobs|alchemy|terminal) ;; *) echo "unknown eval-task: $e"; exit 1 ;; esac; done + +name_of() { case "$1" in frozen_lake_obscure) echo qwen3-8b-flobs ;; mixed_flobs_alchran) echo qwen3-8b-mixed ;; esac; } +cfg_of() { # -> bench config name (alchemy difficulty follows the train task) + case "$2" in + flobs) echo eval_frozenlake_obscure_hard ;; + alchemy) case "$1" in frozen_lake_obscure) echo eval_alchemy_easy ;; mixed_flobs_alchran) echo eval_alchemy_hard ;; esac ;; + terminal) echo eval_terminal ;; + esac +} + +TOTAL=$(( ${#TRAIN_TASKS[@]} * ${#EVAL_TASKS[@]} )); DONE=0 +echo "############ run_eval: train=[${TRAIN_TASKS[*]}] eval=[${EVAL_TASKS[*]}] -> $TOTAL jobs ############" +for t in "${TRAIN_TASKS[@]}"; do + NAME=$(name_of "$t") + for e in "${EVAL_TASKS[@]}"; do + cfg=$(cfg_of "$t" "$e"); DONE=$((DONE + 1)) + echo ">>> [$DONE/$TOTAL] train=$t name=$NAME eval=$e seed=$SEED cfg=$cfg" + EVAL_GROUP="$t" EVAL_NAME="$NAME" EVAL_SEED="$SEED" trinity run --config "$B/$cfg.yaml" || echo " !!! FAILED: train=$t eval=$e" + done +done +echo "############ done: $DONE/$TOTAL ############" diff --git a/examples/research_cod/exp_plan_final/train/alchemy_abl_no_adaptive_red.yaml b/examples/research_cod/exp_plan_final/train/alchemy_abl_no_adaptive_red.yaml new file mode 100644 index 00000000000..62dcd713831 --- /dev/null +++ b/examples/research_cod/exp_plan_final/train/alchemy_abl_no_adaptive_red.yaml @@ -0,0 +1,134 @@ +# RL ablation: train on Alchemy-Random, pack=4. Removes adaptive RED-Weight +# (red_weight_adaptive_temp / red_weight_adaptive_version) to verify its effect. +# Data prep: python examples/research_cod/get_alchemy_data.py --local_dir examples/research_cod/data/alchemy_random --train_size 50000 --test_size 4000 --seed 42 +project: "trinity-cod-final" +group: "alchemy_abl_no_adaptive_red" +name: "qwen3-8b-alchemy" +mode: both +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + cod_workflow_args: + hint_example: false + activated_cod_methods: ["iterative_hint_e2e"] + reuse_workflow_instance: false + max_response_tokens_restraint: 1000 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name} + inject_pack_seed: true + pad_tasks_to_full_pack: true +algorithm: + algorithm_type: multi_step_grpo + sample_strategy: staleness_control + sample_strategy_args: + max_staleness: 20 + repeat_times: 8 + optimizer: + lr: 1e-6 + advantage_fn: cod + advantage_fn_args: + std_cal_level: "none" + iterative_hint_e2e_causal: true + policy_loss_fn: rec + policy_loss_fn_args: # REC-OneSide-NoIS + epsilon_low: 0.2 + epsilon_high: 0.2 + clip_mode: "one-side" + weight: "none" + kl_loss_fn: k2 + kl_loss_fn_args: + kl_coef: 0.0 +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 10000 + max_response_tokens: 2000 + max_model_len: 12000 +cluster: + node_num: 3 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + train_batch_size: 1440 + explorer_input: + tasksets: + - name: alchemy_random + storage_type: file + path: "examples/research_cod/data/alchemy_random" + split: train + workflow_args: + context_compression_mode: "keep_all" + # length penalty in reward design + length_penalty_coef: 0.1 + len_full_penalty: 2000000 + len_zero_penalty: 1000000 + # specific to alchemy-random + max_rounds: 8 + material_mode: "unlimited" + scope: "per_pack" + show_recipes: false + show_elements: false + show_tiers: true + min_num_tiers: 3 + max_num_tiers: 4 + min_base_elements: 3 + max_base_elements: 4 + tier_shrink_min: 0.4 + tier_shrink_max: 0.6 + min_recipes_per_element: 1 + max_recipes_per_element: 1 + max_tier_gap: 1 + min_cross_tier_prob: 0.0 + max_cross_tier_prob: 0.1 + min_noise_node_ratio: 0.0 + max_noise_node_ratio: 0.0 + noise_chain_depth: 2 + rollout_args: + temperature: 1.0 + default_workflow_type: 'cod_random_alchemy_workflow' + trainer_input: + experience_buffer: + name: buffer + storage_type: queue + max_read_timeout: 72000 + replay_buffer: + enable: true + priority_fn: decay_limit_randomization + priority_fn_args: + sigma: 2.0 +explorer: + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: 42 +trainer: + save_interval: 100 + grad_clip: 1.0 + use_dynamic_bsz: true + max_token_len_per_gpu: 16384 + ulysses_sequence_parallel_size: 1 + trainer_config: + actor_rollout_ref: + actor: + checkpoint: + load_contents: ['model', 'extra'] + save_contents: ['model', 'extra'] +synchronizer: + sync_style: dynamic_by_explorer + sync_method: 'nccl' + sync_interval: 4 + sync_timeout: 72000 +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/train/alchemy_abl_no_adaptive_red_is.yaml b/examples/research_cod/exp_plan_final/train/alchemy_abl_no_adaptive_red_is.yaml new file mode 100644 index 00000000000..e3fa3517619 --- /dev/null +++ b/examples/research_cod/exp_plan_final/train/alchemy_abl_no_adaptive_red_is.yaml @@ -0,0 +1,135 @@ +# RL ablation: train on Alchemy-Random, pack=4. Builds on alchemy_abl_no_adaptive_red.yaml (adaptive +# RED-Weight already removed) and additionally sets policy_loss_fn_args.weight "none" -> "importance_sampling" +# to verify the rationale of discarding IS. +# Data prep: python examples/research_cod/get_alchemy_data.py --local_dir examples/research_cod/data/alchemy_random --train_size 50000 --test_size 4000 --seed 42 +project: "trinity-cod-final" +group: "alchemy_abl_no_adaptive_red_is" +name: "qwen3-8b-alchemy" +mode: both +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + cod_workflow_args: + hint_example: false + activated_cod_methods: ["iterative_hint_e2e"] + reuse_workflow_instance: false + max_response_tokens_restraint: 1000 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name} + inject_pack_seed: true + pad_tasks_to_full_pack: true +algorithm: + algorithm_type: multi_step_grpo + sample_strategy: staleness_control + sample_strategy_args: + max_staleness: 20 + repeat_times: 8 + optimizer: + lr: 1e-6 + advantage_fn: cod + advantage_fn_args: + std_cal_level: "none" + iterative_hint_e2e_causal: true + policy_loss_fn: rec + policy_loss_fn_args: # REC-OneSide-IS + epsilon_low: 0.2 + epsilon_high: 0.2 + clip_mode: "one-side" + weight: "importance_sampling" + kl_loss_fn: k2 + kl_loss_fn_args: + kl_coef: 0.0 +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 10000 + max_response_tokens: 2000 + max_model_len: 12000 +cluster: + node_num: 3 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + train_batch_size: 1440 + explorer_input: + tasksets: + - name: alchemy_random + storage_type: file + path: "examples/research_cod/data/alchemy_random" + split: train + workflow_args: + context_compression_mode: "keep_all" + # length penalty in reward design + length_penalty_coef: 0.1 + len_full_penalty: 2000000 + len_zero_penalty: 1000000 + # specific to alchemy-random + max_rounds: 8 + material_mode: "unlimited" + scope: "per_pack" + show_recipes: false + show_elements: false + show_tiers: true + min_num_tiers: 3 + max_num_tiers: 4 + min_base_elements: 3 + max_base_elements: 4 + tier_shrink_min: 0.4 + tier_shrink_max: 0.6 + min_recipes_per_element: 1 + max_recipes_per_element: 1 + max_tier_gap: 1 + min_cross_tier_prob: 0.0 + max_cross_tier_prob: 0.1 + min_noise_node_ratio: 0.0 + max_noise_node_ratio: 0.0 + noise_chain_depth: 2 + rollout_args: + temperature: 1.0 + default_workflow_type: 'cod_random_alchemy_workflow' + trainer_input: + experience_buffer: + name: buffer + storage_type: queue + max_read_timeout: 72000 + replay_buffer: + enable: true + priority_fn: decay_limit_randomization + priority_fn_args: + sigma: 2.0 +explorer: + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: 42 +trainer: + save_interval: 100 + grad_clip: 1.0 + use_dynamic_bsz: true + max_token_len_per_gpu: 16384 + ulysses_sequence_parallel_size: 1 + trainer_config: + actor_rollout_ref: + actor: + checkpoint: + load_contents: ['model', 'extra'] + save_contents: ['model', 'extra'] +synchronizer: + sync_style: dynamic_by_explorer + sync_method: 'nccl' + sync_interval: 4 + sync_timeout: 72000 +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/train/frozen_lake_obscure.yaml b/examples/research_cod/exp_plan_final/train/frozen_lake_obscure.yaml new file mode 100644 index 00000000000..ddbbe4b7c4d --- /dev/null +++ b/examples/research_cod/exp_plan_final/train/frozen_lake_obscure.yaml @@ -0,0 +1,120 @@ +# Data prep: +# python examples/research_cod/get_frozen_lake_data.py --local_dir examples/research_cod/data/frozen_lake_4567 --train_size 50000 --test_size 4000 --map_min_size 4 --map_max_size 5 --tile_min_prob 0.6 --tile_max_prob 0.7 +project: "trinity-cod-final" +group: "frozen_lake_obscure" +name: "qwen3-8b-flobs" +mode: both +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + cod_workflow_args: + hint_example: false + activated_cod_methods: ["iterative_hint_e2e"] + reuse_workflow_instance: false + max_response_tokens_restraint: 1000 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name} + inject_pack_seed: true + pad_tasks_to_full_pack: true +algorithm: + algorithm_type: multi_step_grpo + sample_strategy: staleness_control + sample_strategy_args: + max_staleness: 20 + repeat_times: 8 + optimizer: + lr: 1e-6 + advantage_fn: cod + advantage_fn_args: + std_cal_level: "none" + iterative_hint_e2e_causal: true + red_weight_adaptive_temp: true + red_weight_adaptive_version: "bisection" # "halving" / "bisection" / "step_function" + policy_loss_fn: rec + policy_loss_fn_args: # REC-OneSide-NoIS + epsilon_low: 0.2 + epsilon_high: 0.2 + clip_mode: "one-side" + weight: "none" + kl_loss_fn: k2 + kl_loss_fn_args: + kl_coef: 0.0 +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 10000 + max_response_tokens: 2000 + max_model_len: 12000 +cluster: + node_num: 3 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + train_batch_size: 1440 + explorer_input: + tasksets: + - name: frozenlake_obscure + storage_type: file + path: "examples/research_cod/data/frozen_lake_4567" + split: train + format: + prompt_key: 'task_desc' + workflow_args: + context_compression_mode: "keep_all" + # length penalty in reward design + length_penalty_coef: 0.1 + len_full_penalty: 2000000 + len_zero_penalty: 1000000 + # specific to frozenlake-obscure + env_max_steps: 6 + agent_max_steps: 8 + mapping_mode: "per_pack" + rollout_args: + temperature: 1.0 + default_workflow_type: 'cod_frozenlake_obscure_workflow' + trainer_input: + experience_buffer: + name: buffer + storage_type: queue + max_read_timeout: 72000 + replay_buffer: + enable: true + priority_fn: decay_limit_randomization + priority_fn_args: + sigma: 2.0 +explorer: + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: 42 +trainer: + save_interval: 100 + grad_clip: 1.0 + use_dynamic_bsz: true + max_token_len_per_gpu: 16384 + ulysses_sequence_parallel_size: 1 + trainer_config: + actor_rollout_ref: + actor: + checkpoint: + load_contents: ['model', 'extra'] + save_contents: ['model', 'extra'] +synchronizer: + sync_style: dynamic_by_explorer + sync_method: 'nccl' + sync_interval: 4 + sync_timeout: 72000 +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/train/grid_navigation.yaml b/examples/research_cod/exp_plan_final/train/grid_navigation.yaml new file mode 100644 index 00000000000..cd66264209c --- /dev/null +++ b/examples/research_cod/exp_plan_final/train/grid_navigation.yaml @@ -0,0 +1,122 @@ +# Data prep: +# python examples/research_cod/get_grid_navigation_data.py --local_dir examples/research_cod/data/grid_navigation --train_size 50000 --test_size 4000 --seed 42 +project: ${oc.env:TRINITY_PROJECT,trinity-cod-final} +group: "grid_navigation" +name: "test-qwen3-8b" +mode: both +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + cod_workflow_args: + hint_example: false + activated_cod_methods: ["iterative_hint_e2e"] + reuse_workflow_instance: false + max_response_tokens_restraint: 1000 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name} + inject_pack_seed: true + pad_tasks_to_full_pack: true +algorithm: + algorithm_type: multi_step_grpo + sample_strategy: staleness_control + sample_strategy_args: + max_staleness: 20 + repeat_times: 8 + optimizer: + lr: 1e-6 + lr_warmup_steps: 10 + lr_scheduler_type: constant + advantage_fn: cod + advantage_fn_args: + std_cal_level: "none" + iterative_hint_e2e_causal: true + red_weight_adaptive_temp: true + red_weight_adaptive_version: "bisection" # "halving" / "bisection" / "step_function" + policy_loss_fn: rec + policy_loss_fn_args: # REC-OneSide-NoIS + epsilon_low: 0.2 + epsilon_high: 0.2 + clip_mode: "one-side" + weight: "none" + kl_loss_fn: k2 + kl_loss_fn_args: + kl_coef: 0.0 +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 10000 + max_response_tokens: 2000 + max_model_len: 12000 +cluster: + node_num: 3 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + train_batch_size: 1440 + explorer_input: + tasksets: + - name: grid_navigation + storage_type: file + path: "examples/research_cod/data/grid_navigation" + split: train + workflow_args: + context_compression_mode: "keep_all" + length_penalty_coef: 0.0 + grid_min_size: 8 + grid_max_size: 12 + landscape_num_components: 4 + landscape_min_scale: 0.10 + landscape_max_scale: 0.35 + min_rounds: 4 + max_rounds: 6 + reveal_radius: 2 + rollout_args: + temperature: 1.0 + default_workflow_type: 'cod_grid_navigation_workflow' + trainer_input: + experience_buffer: + name: buffer + storage_type: queue + max_read_timeout: 72000 + replay_buffer: + enable: true + priority_fn: decay_limit_randomization + priority_fn_args: + sigma: 2.0 +explorer: + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: 42 +trainer: + save_interval: 100 + enable_preview: true + grad_clip: 1.0 + use_dynamic_bsz: true + max_token_len_per_gpu: 16384 + ulysses_sequence_parallel_size: 1 + trainer_config: + actor_rollout_ref: + actor: + checkpoint: + load_contents: ['model', 'extra'] + save_contents: ['model', 'extra'] +synchronizer: + sync_style: dynamic_by_explorer + sync_method: 'nccl' + sync_interval: 4 + sync_timeout: 72000 +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/train/grid_navigation_27b.yaml b/examples/research_cod/exp_plan_final/train/grid_navigation_27b.yaml new file mode 100644 index 00000000000..2aa43344c03 --- /dev/null +++ b/examples/research_cod/exp_plan_final/train/grid_navigation_27b.yaml @@ -0,0 +1,128 @@ +# Data prep: +# python examples/research_cod/get_grid_navigation_data.py --local_dir examples/research_cod/data/grid_navigation --train_size 50000 --test_size 4000 --seed 42 +project: ${oc.env:TRINITY_PROJECT,trinity-cod-final} +group: "grid_navigation" +name: "qwen3.6-27b-20260901" +mode: both +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + cod_workflow_args: + hint_example: false + activated_cod_methods: ["iterative_hint_e2e"] + reuse_workflow_instance: false + max_response_tokens_restraint: 4000 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name} + inject_pack_seed: true + pad_tasks_to_full_pack: true +algorithm: + algorithm_type: multi_step_grpo + sample_strategy: staleness_control + sample_strategy_args: + max_staleness: 20 + repeat_times: 8 + optimizer: + lr: 1e-6 + lr_warmup_steps: 10 + lr_scheduler_type: constant + advantage_fn: cod + advantage_fn_args: + std_cal_level: "none" + iterative_hint_e2e_causal: true + red_weight_adaptive_temp: true + red_weight_adaptive_version: "bisection" # "halving" / "bisection" / "step_function" + policy_loss_fn: rec + policy_loss_fn_args: # REC-OneSide-NoIS + epsilon_low: 0.2 + epsilon_high: 0.2 + clip_mode: "one-side" + weight: "none" + kl_loss_fn: k2 + kl_loss_fn_args: + kl_coef: 0.0 +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 22000 + max_response_tokens: 3999 + max_model_len: 26000 +cluster: + node_num: 3 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + train_batch_size: 1440 + explorer_input: + tasksets: + - name: grid_navigation + storage_type: file + path: "examples/research_cod/data/grid_navigation" + split: train + workflow_args: + context_compression_mode: "keep_all" + length_penalty_coef: 0.0 + grid_min_size: 8 + grid_max_size: 12 + landscape_num_components: 4 + landscape_min_scale: 0.15 + landscape_max_scale: 0.45 + min_rounds: 3 + max_rounds: 5 + reveal_radius: 1 + rollout_args: + temperature: 1.0 + default_workflow_type: 'cod_grid_navigation_workflow' + trainer_input: + experience_buffer: + name: buffer + storage_type: queue + max_read_timeout: 72000 + replay_buffer: + enable: true + priority_fn: decay_limit_randomization + priority_fn_args: + sigma: 2.0 +explorer: + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: 42 +trainer: + save_interval: 20 + enable_preview: true + grad_clip: 1.0 + use_dynamic_bsz: true + use_remove_padding: true + # max_token_len_per_gpu: 16384 + ulysses_sequence_parallel_size: 4 + # fix_actor_microbatch_loss_scale: true + trainer_config: + actor_rollout_ref: + model: + use_fused_kernels: ${oc.decode:${oc.env:FUSED_KERNELS,true}} + fused_kernel_options: + impl_backend: torch + actor: + checkpoint: + load_contents: ['model', 'extra'] + save_contents: ['model', 'extra'] +synchronizer: + sync_style: dynamic_by_explorer + sync_method: 'nccl' + sync_interval: 4 + sync_timeout: 72000 +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/train/mixed_flobs_alchran.yaml b/examples/research_cod/exp_plan_final/train/mixed_flobs_alchran.yaml new file mode 100644 index 00000000000..a69838b0399 --- /dev/null +++ b/examples/research_cod/exp_plan_final/train/mixed_flobs_alchran.yaml @@ -0,0 +1,155 @@ +# Data prep (joint training on FrozenLake-Obscure + Alchemy-Random): +# python examples/research_cod/get_frozen_lake_data.py --local_dir examples/research_cod/data/frozen_lake_4567 --train_size 50000 --test_size 4000 --map_min_size 4 --map_max_size 5 --tile_min_prob 0.6 --tile_max_prob 0.7 +# python examples/research_cod/get_alchemy_data.py --local_dir examples/research_cod/data/alchemy_random --train_size 50000 --test_size 4000 --seed 42 +project: "trinity-cod-final" +group: "mixed_flobs_alchran" +name: "qwen3-8b-mixed" +mode: both +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + cod_workflow_args: + hint_example: false + activated_cod_methods: ["iterative_hint_e2e"] + reuse_workflow_instance: false + max_response_tokens_restraint: 1000 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name} + inject_pack_seed: true + pad_tasks_to_full_pack: true +algorithm: + algorithm_type: multi_step_grpo + sample_strategy: staleness_control + sample_strategy_args: + max_staleness: 20 + repeat_times: 8 + optimizer: + lr: 1e-6 + advantage_fn: cod + advantage_fn_args: + std_cal_level: "none" + iterative_hint_e2e_causal: true + red_weight_adaptive_temp: true + red_weight_adaptive_version: "bisection" # "halving" / "bisection" / "step_function" + policy_loss_fn: rec + policy_loss_fn_args: # REC-OneSide-NoIS + epsilon_low: 0.2 + epsilon_high: 0.2 + clip_mode: "one-side" + weight: "none" + kl_loss_fn: k2 + kl_loss_fn_args: + kl_coef: 0.0 +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 10000 + max_response_tokens: 2000 + max_model_len: 12000 +cluster: + node_num: 4 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + train_batch_size: 1440 + explorer_input: + tasksets: + - name: frozenlake_obscure + storage_type: file + path: "examples/research_cod/data/frozen_lake_4567" + split: train + format: + prompt_key: 'task_desc' + workflow_args: + context_compression_mode: "keep_all" + # length penalty in reward design + length_penalty_coef: 0.1 + len_full_penalty: 2000000 + len_zero_penalty: 1000000 + # specific to frozenlake-obscure + env_max_steps: 6 + agent_max_steps: 8 + mapping_mode: "per_pack" + rollout_args: + temperature: 1.0 + default_workflow_type: 'cod_frozenlake_obscure_workflow' + - name: alchemy_random + storage_type: file + path: "examples/research_cod/data/alchemy_random" + split: train + workflow_args: + context_compression_mode: "keep_all" + # length penalty in reward design + length_penalty_coef: 0.1 + len_full_penalty: 2000000 # 1500 + len_zero_penalty: 1000000 # 500 + # specific to alchemy-random + max_rounds: 8 + material_mode: "unlimited" + scope: "per_pack" + show_recipes: false + show_elements: false + show_tiers: true + min_num_tiers: 3 + max_num_tiers: 4 + min_base_elements: 3 + max_base_elements: 4 + tier_shrink_min: 0.4 + tier_shrink_max: 0.6 + min_recipes_per_element: 1 + max_recipes_per_element: 1 + max_tier_gap: 1 + min_cross_tier_prob: 0.0 + max_cross_tier_prob: 0.1 + min_noise_node_ratio: 0.0 + max_noise_node_ratio: 0.0 + noise_chain_depth: 2 + rollout_args: + temperature: 1.0 + default_workflow_type: 'cod_random_alchemy_workflow' + trainer_input: + experience_buffer: + name: buffer + storage_type: queue + max_read_timeout: 72000 + replay_buffer: + enable: true + priority_fn: decay_limit_randomization + priority_fn_args: + sigma: 2.0 +explorer: + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: 42 +trainer: + save_interval: 100 + grad_clip: 1.0 + use_dynamic_bsz: true + max_token_len_per_gpu: 16384 + ulysses_sequence_parallel_size: 1 + trainer_config: + actor_rollout_ref: + actor: + checkpoint: + load_contents: ['model', 'extra'] + save_contents: ['model', 'extra'] +synchronizer: + sync_style: dynamic_by_explorer + sync_method: 'nccl' + sync_interval: 4 + sync_timeout: 72000 +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/train/optimal_control_improve.yaml b/examples/research_cod/exp_plan_final/train/optimal_control_improve.yaml new file mode 100644 index 00000000000..bfb41fb0466 --- /dev/null +++ b/examples/research_cod/exp_plan_final/train/optimal_control_improve.yaml @@ -0,0 +1,130 @@ +# Data prep: +# python examples/research_cod/get_optimal_control_data.py \ +# --local_dir examples/research_cod/data/optimal_control \ +# --train_size 50000 --test_size 4000 --difficulty hard +# Before training, install the Qwen3.6 kernels as documented in examples/research_cod/README.md. +# export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True +project: "trinity-cod" +group: "optimal_control" +name: ${oc.env:EXP_NAME,qwen3.6-27b-optimal-control-cod} +mode: both +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false +cod: + task_pack_size: 4 + packing_strategy: cod + cod_workflow_args: + hint_example: false + activated_cod_methods: ["iterative_hint_e2e"] + reuse_workflow_instance: false + max_response_tokens_restraint: 4000 + hint_penalty_coef: 0.1 + cod_log_interval: 50 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name} + inject_pack_seed: true + stable_pack_seed: true + pad_tasks_to_full_pack: true +algorithm: + algorithm_type: multi_step_grpo + sample_strategy: staleness_control + sample_strategy_args: + max_staleness: 20 + repeat_times: 8 + optimizer: + lr: 1e-6 + advantage_fn: cod + advantage_fn_args: + std_cal_level: "none" + iterative_hint_e2e_causal: true + red_weight_adaptive_temp: true + red_weight_adaptive_version: "bisection" + policy_loss_fn: rec + policy_loss_fn_args: + epsilon_low: 0.2 + epsilon_high: 0.2 + clip_mode: "one-side" + weight: "none" + kl_loss_fn: k2 + kl_loss_fn_args: + kl_coef: 0.0 +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 12000 + max_response_tokens: 4000 + max_model_len: 40000 +cluster: + node_num: 3 + gpu_per_node: 8 +buffer: + total_epochs: 1 + batch_size: 32 + train_batch_size: 480 + explorer_input: + tasksets: + - name: optimal_control + storage_type: file + path: "examples/research_cod/data/optimal_control" + split: train + format: + prompt_key: 'task_desc' + workflow_args: + context_compression_mode: "keep_all" + include_previous_control_policy: true + a_env_range: [0.55, 1.20] + b_env_range: [-1.5, 1.5] + min_abs_b_env: 0.35 + enable_process_noise: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + default_workflow_type: 'cod_optimalcontrol_workflow' + trainer_input: + experience_buffer: + name: buffer + storage_type: queue + max_read_timeout: 72000 + replay_buffer: + enable: true + priority_fn: decay_limit_randomization + priority_fn_args: + sigma: 2.0 +explorer: + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: 42 +trainer: + save_interval: 100 + enable_preview: true + grad_clip: 1.0 + use_dynamic_bsz: ${oc.env:USE_DYNAMIC_BSZ,true} + use_remove_padding: ${oc.env:REMOVE_PADDING,true} + ulysses_sequence_parallel_size: ${oc.env:SP_SIZE,4} + fix_actor_microbatch_loss_scale: ${oc.env:FIX_LOSS_SCALE,true} + trainer_config: + actor_rollout_ref: + model: + use_fused_kernels: ${oc.decode:${oc.env:FUSED_KERNELS,true}} + fused_kernel_options: + impl_backend: torch + actor: + checkpoint: + load_contents: ['model', 'extra'] + save_contents: ['model', 'extra'] +synchronizer: + sync_style: dynamic_by_explorer + sync_method: 'nccl' + sync_interval: 4 + sync_timeout: 72000 +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/train/pde_control_grid_opd.yaml b/examples/research_cod/exp_plan_final/train/pde_control_grid_opd.yaml new file mode 100644 index 00000000000..6d9896b5bae --- /dev/null +++ b/examples/research_cod/exp_plan_final/train/pde_control_grid_opd.yaml @@ -0,0 +1,235 @@ +# Multi-teacher OPD on PDE, optimal-control, and grid-navigation tasksets. +# Data prep: +# python examples/research_cod/get_pde_discovery_data.py --local_dir examples/research_cod/data/pde_discovery_runtime_seed --train_size 50000 --test_size 4000 --seed 42 +# python examples/research_cod/get_optimal_control_data.py --local_dir examples/research_cod/data/optimal_control --train_size 50000 --test_size 4000 --difficulty hard --train_seed 42 --test_seed 2024 +# python examples/research_cod/get_grid_navigation_data.py --local_dir examples/research_cod/data/grid_navigation --train_size 50000 --test_size 4000 --seed 42 +# 教师路径指向已转换的 Hugging Face checkpoint,顺序为 PDE、Optimal Control、Grid Navigation。 +# export TRINITY_PDE_TEACHER_MODEL_PATH=/path/to/pde-teacher/actor/huggingface +# export TRINITY_OPTIMAL_CONTROL_TEACHER_MODEL_PATH=/path/to/control-teacher/actor/huggingface +# export TRINITY_GRID_NAVIGATION_TEACHER_MODEL_PATH=/path/to/grid-teacher/actor/huggingface +project: ${oc.env:OPD_PROJECT,trinity-cod} +group: "mixed_multi_teacher_opd" +name: ${oc.env:EXP_NAME,qwen3.6-27b-mixed-opd-pde75-control200-grid100} +mode: both +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false + +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + packing_strategy: cod + cod_workflow_args: + hint_example: false + activated_cod_methods: ["iterative_hint_e2e"] + reuse_workflow_instance: false + enable_teacher_logprobs: true + max_response_tokens_restraint: 4000 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name} + inject_pack_seed: true + stable_pack_seed: true + pad_tasks_to_full_pack: true + +algorithm: + algorithm_type: on_policy_distill + sample_strategy: staleness_control + sample_strategy_args: + max_staleness: 5 + repeat_times: 1 + optimizer: + lr: 1e-6 + lr_warmup_steps: 10 + lr_scheduler_type: constant + advantage_fn_args: + kl_coef: 1.0 + policy_loss_fn: rec + policy_loss_fn_args: + clip_mode: "none" + weight: "none" + fix_opd_advantage: true + kl_loss_fn: k2 + kl_loss_fn_args: + kl_coef: 0.0 + entropy_loss_fn: default + +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + max_prompt_tokens: 22000 + max_response_tokens: 4000 + max_model_len: 26001 + +cluster: + node_num: 3 + gpu_per_node: 8 + +buffer: + total_epochs: 1 + batch_size: 256 + train_batch_size: 720 + explorer_input: + tasksets: + - name: pde_discovery + storage_type: file + path: ${oc.env:TRINITY_PDE_TASKSET_PATH,examples/research_cod/data/pde_discovery_runtime_seed} + split: train + format: + prompt_key: "task_desc" + response_key: "answer" + workflow_args: + update_context_impl: "pde_update_context" + context_compression_mode: "keep_all" + max_steps: 6 + point_budget: 6 + min_reaction_terms: 1 + max_reaction_terms: 2 + ground_truth_family: "physical_full_train.json" + pde_state_abs_limit: 3.0 + initial_condition_shape: + mode_count_range: [1, 2] + noise_level: 0.002 + kappa_threshold: 100.0 + dump_trajectories: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + default_workflow_type: "trinity.common.workflows.connect_the_dots.pde_discovery.workflow.CoDPDEDiscoveryWorkflow" + + - name: optimal_control + storage_type: file + path: ${oc.env:TRINITY_OPTIMAL_CONTROL_TASKSET_PATH,examples/research_cod/data/optimal_control} + split: train + format: + prompt_key: "task_desc" + workflow_args: + update_context_impl: "update_context" + context_compression_mode: "keep_all" + include_previous_control_policy: true + a_env_range: [0.55, 1.20] + b_env_range: [-1.5, 1.5] + min_abs_b_env: 0.35 + enable_process_noise: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + default_workflow_type: "cod_optimalcontrol_workflow" + + - name: grid_navigation + storage_type: file + path: ${oc.env:TRINITY_GRID_NAVIGATION_TASKSET_PATH,examples/research_cod/data/grid_navigation} + split: train + workflow_args: + update_context_impl: "update_context" + context_compression_mode: "keep_all" + length_penalty_coef: 0.0 + grid_min_size: 8 + grid_max_size: 12 + landscape_num_components: 4 + landscape_min_scale: 0.15 + landscape_max_scale: 0.45 + min_rounds: 3 + max_rounds: 5 + reveal_radius: 1 + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + default_workflow_type: "cod_grid_navigation_workflow" + + trainer_input: + experience_buffer: + name: buffer + storage_type: queue + max_read_timeout: 72000 + +explorer: + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 4 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: true + dtype: bfloat16 + seed: 42 + auxiliary_models: + - name: pde_step75_teacher + model_path: ${oc.env:TRINITY_PDE_TEACHER_MODEL_PATH} + engine_type: vllm + engine_num: 4 + tensor_parallel_size: 1 + max_prompt_tokens: 22000 + max_response_tokens: 4000 + max_model_len: 26001 + enable_prefix_caching: false + enforce_eager: true + extra_engine_args: + max_num_seqs: 32 + dtype: bfloat16 + seed: 42 + - name: optimal_control_step200_teacher + model_path: ${oc.env:TRINITY_OPTIMAL_CONTROL_TEACHER_MODEL_PATH} + engine_type: vllm + engine_num: 4 + tensor_parallel_size: 1 + max_prompt_tokens: 22000 + max_response_tokens: 4000 + max_model_len: 26001 + enable_prefix_caching: false + enforce_eager: true + extra_engine_args: + max_num_seqs: 32 + dtype: bfloat16 + seed: 42 + - name: grid_navigation_step100_teacher + model_path: ${oc.env:TRINITY_GRID_NAVIGATION_TEACHER_MODEL_PATH} + engine_type: vllm + engine_num: 4 + tensor_parallel_size: 1 + max_prompt_tokens: 22000 + max_response_tokens: 4000 + max_model_len: 26001 + enable_prefix_caching: false + enforce_eager: true + extra_engine_args: + max_num_seqs: 32 + dtype: bfloat16 + seed: 42 + +trainer: + total_steps: 200 + save_interval: 5 + enable_preview: true + grad_clip: 1.0 + use_dynamic_bsz: ${oc.env:USE_DYNAMIC_BSZ,true} + max_token_len_per_gpu: 7500 + use_remove_padding: ${oc.env:REMOVE_PADDING,true} + ulysses_sequence_parallel_size: 4 + offload_policy: false + # Keep enabled for this run; revisit after inspecting the initial curves. + fix_actor_microbatch_loss_scale: ${oc.env:FIX_LOSS_SCALE,true} + trainer_config: + actor_rollout_ref: + model: + use_fused_kernels: ${oc.decode:${oc.env:FUSED_KERNELS,true}} + fused_kernel_options: + impl_backend: torch + actor: + checkpoint: + load_contents: ["model", "extra"] + save_contents: ["model", "extra"] + +synchronizer: + sync_style: dynamic_by_explorer + sync_method: nccl + sync_interval: 1 + sync_timeout: 72000 + +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_final/train/pde_discovery_cod_600steps_hard.yaml b/examples/research_cod/exp_plan_final/train/pde_discovery_cod_600steps_hard.yaml new file mode 100644 index 00000000000..b7f7422a071 --- /dev/null +++ b/examples/research_cod/exp_plan_final/train/pde_discovery_cod_600steps_hard.yaml @@ -0,0 +1,180 @@ +# Hard, step-bounded rerun of PDE CoD training. +# +# The 50,000-row training split contains about 1,562 explorer batches at +# batch_size=32, so 600 explorer steps consume only 19,200 raw tasks (about +# 38% of one epoch). Based on the previous run's convergence around the 100-step +# region, save every 25 trainer steps and evaluate the saved checkpoints with +# the hard PDE bench configs. Explorer/W&B rollout steps and trainer/checkpoint +# global steps are separate asynchronous counters and are not guaranteed to match. +# +# Data prep: +# python examples/research_cod/get_pde_discovery_data.py --local_dir examples/research_cod/data/pde_discovery_runtime_seed --train_size 50000 --test_size 32 --seed 42 +# Before training, install the Qwen3.6 kernels as documented in examples/research_cod/README.md. +# export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True +project: "trinity-cod" +group: "pde_discovery" +name: ${oc.env:EXP_NAME,qwen3.6-27b-pde-discovery-cod-600steps-hard} +mode: both +checkpoint_root_dir: ${oc.env:TRINITY_CHECKPOINT_ROOT_DIR,./checkpoints} +continue_from_checkpoint: false + +cod: + task_pack_size: 4 + eval_task_pack_size: 8 + packing_strategy: cod + cod_workflow_args: + hint_example: false + activated_cod_methods: ["iterative_hint_e2e"] + update_context_impl: "pde_update_context" + reuse_workflow_instance: false + max_response_tokens_restraint: 4000 + hint_penalty_coef: 0.1 + cod_log_interval: 200 + log_dir: ${oc.env:TRINITY_LOG_DIR,./logs/research_cod} + exp_name: ${project}-${group}-${name} + inject_pack_seed: true + # Derive the runtime pack seed from dataset row identities. This preserves + # flexible runtime packing while keeping each eval pack's hidden PDE fixed + # across checkpoints, making the W&B eval curve directly comparable. + stable_pack_seed: true + pad_tasks_to_full_pack: true + +algorithm: + algorithm_type: multi_step_grpo + sample_strategy: staleness_control + sample_strategy_args: + max_staleness: 20 + repeat_times: 8 + optimizer: + lr: 1e-6 + advantage_fn: cod + advantage_fn_args: + std_cal_level: "none" + iterative_hint_e2e_causal: true + red_weight_adaptive_temp: true + red_weight_adaptive_version: "bisection" + policy_loss_fn: rec + policy_loss_fn_args: + epsilon_low: 0.2 + epsilon_high: 0.2 + clip_mode: "one-side" + weight: "none" + kl_loss_fn: k2 + kl_loss_fn_args: + kl_coef: 0.0 + # Compute and log policy entropy for collapse monitoring, but keep its + # optimization coefficient at zero so it does not change the training objective. + entropy_loss_fn: default + entropy_loss_fn_args: + entropy_coef: 0.0 + +model: + model_path: ${oc.env:TRINITY_MODEL_PATH} + # Prior PDE rollouts peaked around 16,111 prompt tokens. Keep prompt headroom + # and restore the 4,000-token response budget so the model has enough space + # to finish the mandatory XML action after its reasoning. + max_prompt_tokens: 18000 + max_response_tokens: 4000 + max_model_len: 22001 + +cluster: + node_num: 3 + gpu_per_node: 8 + +buffer: + # Caps task production at 600 explorer/rollout batches. In W&B this is the + # step axis used by rollout/* metrics. It takes precedence over + # total_epochs and is intentionally below one epoch. + total_steps: 600 + batch_size: 32 + train_batch_size: 1440 + explorer_input: + tasksets: + - name: pde_discovery + storage_type: file + # A distinct directory prevents accidentally reusing legacy rows that + # already contain pack_seed/task_idx/pack_size. + path: ${oc.env:TRINITY_PDE_TASKSET_PATH,examples/research_cod/data/pde_discovery_runtime_seed} + split: train + format: + prompt_key: 'task_desc' + response_key: 'answer' + workflow_args: + context_compression_mode: "keep_all" + max_steps: 6 + point_budget: 6 + min_reaction_terms: 1 + max_reaction_terms: 2 + ground_truth_family: "physical_full_train.json" + pde_state_abs_limit: 3.0 + initial_condition_shape: + mode_count_range: [1, 2] + noise_level: 0.002 + kappa_threshold: 100.0 + dump_trajectories: false + rollout_args: + temperature: 1.0 + data_selector: + selector_type: sequential + default_workflow_type: 'trinity.common.workflows.connect_the_dots.pde_discovery.workflow.CoDPDEDiscoveryWorkflow' + trainer_input: + experience_buffer: + name: buffer + storage_type: queue + max_read_timeout: 72000 + replay_buffer: + enable: true + priority_fn: decay_limit_randomization + priority_fn_args: + sigma: 2.0 + +explorer: + runner_per_model: 32 + max_repeat_times_per_runner: 1 + max_timeout: 72000 + rollout_model: + enable_thinking: false + enable_history: false + engine_num: 8 + tensor_parallel_size: 1 + enable_prefix_caching: false + enforce_eager: false + dtype: bfloat16 + seed: 42 + +trainer: + # This interval is measured in trainer global steps, not explorer steps. + # A final checkpoint is saved when training drains, and the default "last" + # policy exports that final checkpoint in Hugging Face format. + save_interval: 25 + enable_preview: true + grad_clip: 1.0 + use_dynamic_bsz: ${oc.env:USE_DYNAMIC_BSZ,true} + # A maximum-length sequence contributes ceil(22001 / SP4)=5501 tokens per + # trainer GPU. Sixteen trainer GPUs without offload peaked at about 51 GB + # with a 6500-token budget, leaving room for a larger dynamic micro-batch. + # The larger budget can reduce the micro-batch count and can be overridden + # at launch if later batches show a higher memory peak. + max_token_len_per_gpu: ${oc.env:MAX_TOKEN_LEN_PER_GPU,13000} + use_remove_padding: ${oc.env:REMOVE_PADDING,true} + ulysses_sequence_parallel_size: ${oc.env:SP_SIZE,4} + fix_actor_microbatch_loss_scale: ${oc.env:FIX_LOSS_SCALE,true} + trainer_config: + actor_rollout_ref: + model: + use_fused_kernels: ${oc.decode:${oc.env:FUSED_KERNELS,true}} + fused_kernel_options: + impl_backend: torch + actor: + checkpoint: + load_contents: ['model', 'extra'] + save_contents: ['model', 'extra'] + +synchronizer: + sync_style: dynamic_by_explorer + sync_method: 'nccl' + sync_interval: 4 + sync_timeout: 72000 + +monitor: + monitor_type: wandb diff --git a/examples/research_cod/exp_plan_learn2ask/data_prepare/1_info_extract_pipeline.py b/examples/research_cod/exp_plan_learn2ask/data_prepare/1_info_extract_pipeline.py new file mode 100644 index 00000000000..9fff6a9020d --- /dev/null +++ b/examples/research_cod/exp_plan_learn2ask/data_prepare/1_info_extract_pipeline.py @@ -0,0 +1,288 @@ +import argparse +import json +import os +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[4])) + +from examples.learn_to_ask.data_prepare.message_splitter import split_session_to_json_lines +from examples.research_cod.exp_plan_learn2ask.data_prepare.llm_info_extraction import ( + LLM_info_extraction_batch, + parse_llm_output, +) + + +def process_jsonl_file( + input_file, output_file, model_call_mode="online_api", max_retries=3, **kwargs +): + """Batched info extraction: collect every cid's prompt from every + session, feed the whole lot to the backend in one llm.generate call + (for local_vllm) so vLLM's continuous batching can do the work. + + Failed cids are retried in additional batched rounds; at most + max_retries + 1 rounds total. Surviving failures get info_set=None + (2_build_dataset drops those with decision=continue). + + Args: + input_file (str): path to input jsonl (one session per line). + output_file (str): path to output jsonl (one cid per line, with + `info_set` filled in). + model_call_mode (str): "online_api" or "local_vllm". + max_retries (int): extra rounds of batched retry on top of the + first attempt. + **kwargs: forwarded to the backend (model_path, enable_thinking, + tensor_parallel_size, etc.). + """ + # -- Load all sessions -- + sessions = [] + with open(input_file, "r", encoding="utf-8") as infile: + for line_num, line in enumerate(infile, 1): + line = line.strip() + if not line: + continue + try: + sessions.append(json.loads(line)) + except json.JSONDecodeError as e: + print(f"Warning: Skipping invalid JSON at line {line_num}: {e}") + print(f"[load] {len(sessions)} sessions from {input_file}") + + # -- Split every session into cids, keep flat + nested views -- + per_session_cids = [] # list[list[dict]] preserving session order + flat_data = [] # flat list of cid dicts (same objects as nested) + remaining_chats = [] # flat list of remaining_chat strings + + for session in sessions: + session_cids = [] + for cid_json in split_session_to_json_lines(session): + data = json.loads(cid_json) + data.setdefault("info_set", None) + session_cids.append(data) + flat_data.append(data) + remaining_chats.append(data.get("remaining_chat", "")) + per_session_cids.append(session_cids) + + total = len(flat_data) + print(f"[flatten] {total} cids across {len(sessions)} sessions") + if total == 0: + # Still emit an empty output so downstream doesn't trip over a missing file. + open(output_file, "w", encoding="utf-8").close() + return f"Nothing to process; wrote empty file {output_file}" + + # -- Batched generation with multi-round retry -- + pending = list(range(total)) + for round_idx in range(max_retries + 1): + if not pending: + break + print( + f"[batch] round {round_idx + 1}/{max_retries + 1}: " + f"generating for {len(pending)} cids" + ) + chats_to_run = [remaining_chats[i] for i in pending] + outputs = LLM_info_extraction_batch( + chats_to_run, model_call_mode, **kwargs + ) + + still_pending = [] + for orig_idx, text in zip(pending, outputs): + parsed = parse_llm_output(text) + if isinstance(parsed, list): + flat_data[orig_idx]["info_set"] = parsed + else: + still_pending.append(orig_idx) + print( + f"[batch] round {round_idx + 1}: " + f"{len(pending) - len(still_pending)} succeeded, " + f"{len(still_pending)} still pending" + ) + pending = still_pending + + if pending: + print( + f"[batch] WARNING: {len(pending)} cids still failed after " + f"{max_retries + 1} rounds; leaving info_set=None" + ) + + # -- Write output in session-major order (session 0's cids, then 1's, ...) -- + with open(output_file, "w", encoding="utf-8") as outf: + for session_cids in per_session_cids: + for data in session_cids: + outf.write(json.dumps(data, ensure_ascii=False) + "\n") + + return f"Successfully processed {total} cids. Results saved to {output_file}" + + +# Example usage: +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "--input_file", type=str, default="examples/learn_to_ask/data_raw/train_origin.jsonl" + ) + parser.add_argument( + "--output_file", type=str, default="examples/learn_to_ask/data_raw/train_processed.jsonl" + ) + parser.add_argument( + "--model_call_mode", type=str, choices=["online_api", "local_vllm"], default="local_vllm" + ) + parser.add_argument("--model_path", type=str, required=True) + parser.add_argument( + "--enable_thinking", + choices=["true", "false"], + default=None, + help=( + "For Qwen3-family local_vllm runs, explicitly toggle the chat " + "template's thinking mode. Omit to use the model default (which " + "is True for Qwen3, producing ... output that " + "breaks ast.literal_eval downstream — pass 'false' for extraction)." + ), + ) + parser.add_argument( + "--dataset_id", + type=str, + default=None, + help=( + "Dataset id on HF or ModelScope (e.g. 'JasonHaggard/RealMedConv'). When set, " + "--input_file is treated as a *destination path* for dumping the " + "HF split to jsonl before extraction, instead of an existing file." + ), + ) + parser.add_argument( + "--hf_split", + type=str, + default=None, + help="HF split name (required with --dataset_id), e.g. 'train' / 'test'. " + "Use --hf_splits instead to process several splits with a single engine.", + ) + parser.add_argument( + "--hf_splits", + nargs="+", + default=None, + help=( + "Multiple HF split names (e.g. 'train test') to process in ONE " + "process so vLLM engine is initialised only once. Overrides " + "--hf_split. When --dataset_id is given but neither --hf_split " + "nor --hf_splits is set, defaults to ['train', 'test']." + ), + ) + parser.add_argument( + "--output_template", + type=str, + default="examples/research_cod/data/learn2ask_artifacts/{split}_processed.jsonl", + help=( + "Output path template used in multi-split mode; must contain the " + "literal '{split}'. Default: " + "examples/research_cod/data/learn2ask_artifacts/{split}_processed.jsonl. " + "Kept out of examples/research_cod/data/learn2ask/ because HF " + "datasets 4.x treats that dir as the training base_path and its " + "recursive ** glob would sweep every train_*/test_*.jsonl into " + "the respective split." + ), + ) + parser.add_argument( + "--max_retries", + type=int, + default=3, + help="Extra rounds of batched retry on top of the first attempt (default: 3).", + ) + parser.add_argument( + "--tensor_parallel_size", + type=int, + default=1, + help="GPUs to shard one model across (vLLM tensor parallelism). For " + "single-process offline use, set to your GPU count (e.g. 8) to use all " + "cards; default: 1.", + ) + parser.add_argument( + "--data_parallel_size", + type=int, + default=1, + help="vLLM data parallelism (independent replicas). The single-process " + "offline LLM only supports 1; >1 raises ValueError and needs the " + "multi-process launcher. Default: 1.", + ) + parser.add_argument( + "--dataset_source", + choices=["hf", "modelscope"], + default="modelscope", + help="Download --dataset_id from HuggingFace or ModelScope (default: modelscope).", + ) + args = parser.parse_args() + + extra_kwargs = { + "tensor_parallel_size": args.tensor_parallel_size, + "data_parallel_size": args.data_parallel_size, + } + if args.enable_thinking is not None: + extra_kwargs["enable_thinking"] = args.enable_thinking == "true" + + # Default to ['train', 'test'] in one shot when the caller gave + # --dataset_id but did not pin down a single split. + if ( + args.dataset_id is not None + and args.hf_splits is None + and args.hf_split is None + ): + args.hf_splits = ["train", "test"] + + # -- Multi-split HF mode: one engine for all splits. -- + # Inputs (HF split dumps) go next to the outputs so the data prep + # directory holds everything. + if args.hf_splits: + assert args.dataset_id is not None, "--hf_splits requires --dataset_id" + assert ( + args.output_template and "{split}" in args.output_template + ), "--hf_splits requires --output_template containing '{split}'" + for split in args.hf_splits: + output_path = args.output_template.replace("{split}", split) + input_path = os.path.join( + os.path.dirname(os.path.abspath(output_path)), + f"{split}_raw.jsonl", + ) + os.makedirs(os.path.dirname(os.path.abspath(input_path)), exist_ok=True) + if args.dataset_source == "modelscope": + from modelscope.msdatasets import MsDataset # lazy import + ds = MsDataset.load(args.dataset_id, split=split) + n_rows = 0 + with open(input_path, "w", encoding="utf-8") as wf: + for item in ds: + wf.write(json.dumps(dict(item), ensure_ascii=False) + "\n") + n_rows += 1 + else: + from datasets import load_dataset # lazy import + ds = load_dataset(args.dataset_id, split=split) + ds.to_json(input_path, lines=True, force_ascii=False) + n_rows = len(ds) + print(f"[{args.dataset_source}] split={split}: {n_rows} rows -> {input_path}") + print( + process_jsonl_file( + input_file=input_path, + output_file=output_path, + model_call_mode=args.model_call_mode, + model_path=args.model_path, + max_retries=args.max_retries, + **extra_kwargs, + ) + ) + else: + # -- Single-split path (backward compatible). -- + if args.dataset_id is not None: + assert args.hf_split is not None, "--hf_split is required with --dataset_id" + from datasets import load_dataset # lazy import + ds = load_dataset(args.dataset_id, split=args.hf_split) + os.makedirs(os.path.dirname(os.path.abspath(args.input_file)), exist_ok=True) + ds.to_json(args.input_file, lines=True, force_ascii=False) + print( + f"[hf] loaded {len(ds)} rows from {args.dataset_id}:{args.hf_split} " + f"and dumped to {args.input_file}" + ) + + print( + process_jsonl_file( + input_file=args.input_file, + output_file=args.output_file, + model_call_mode=args.model_call_mode, + model_path=args.model_path, + max_retries=args.max_retries, + **extra_kwargs, + ) + ) diff --git a/examples/research_cod/exp_plan_learn2ask/data_prepare/2_build_dataset.py b/examples/research_cod/exp_plan_learn2ask/data_prepare/2_build_dataset.py new file mode 100644 index 00000000000..892784bb579 --- /dev/null +++ b/examples/research_cod/exp_plan_learn2ask/data_prepare/2_build_dataset.py @@ -0,0 +1,85 @@ +import argparse +import json +import os + + +def process_message(json_obj): + info_set = json_obj.get("info_set") + info_set_str = ", ".join(info_set) if isinstance(info_set, list) else "" + if "user: " not in json_obj["remaining_chat"]: + decision_str = "stop" + else: + decision_str = "continue" + # KNOWN BIAS: drops 2607 continue tasks (extractor ignores medication / allergy / chronic). + if not info_set_str and decision_str == "continue": + if_keep = False + else: + if_keep = True + return if_keep, info_set_str, decision_str + + +def main(input_file_path, output_file_path): + # Drop user-ending sessions (no doctor reply -> no ground-truth final decision) + # and ghost rounds (remaining_chat empty -> decision point already passed). + by_session = {} + with open(input_file_path, "r", encoding="utf-8") as infile: + for line in infile: + data = json.loads(line.strip()) + by_session.setdefault(data["session_id"], []).append(data) + + n_sess_dropped = n_ghost_dropped = n_info_dropped = n_kept = 0 + with open(output_file_path, "w", encoding="utf-8") as outfile: + for sid, rounds in by_session.items(): + rounds.sort(key=lambda r: r.get("round_number", 0)) + last_msgs = rounds[-1].get("messages") or [] + if last_msgs and last_msgs[-1].get("role") == "user": + n_sess_dropped += 1 + continue + for data in rounds: + if not data.get("remaining_chat"): + n_ghost_dropped += 1 + continue + if_keep, info_set, decision = process_message(data) + if not if_keep: + n_info_dropped += 1 + continue + + new_item = { + "cid": data["cid"], + "session_id": data["session_id"], + "diagn": data["diagn"], + "messages": data["messages"], + "decision_truth": decision, + "info_truth": info_set, + } + outfile.write(json.dumps(new_item, ensure_ascii=False) + "\n") + n_kept += 1 + print( + f"job done! kept={n_kept}, dropped: session={n_sess_dropped}, " + f"ghost_round={n_ghost_dropped}, no_info={n_info_dropped}" + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument( + "--input_template", + type=str, + default="examples/research_cod/data/learn2ask_artifacts/{split}_processed.jsonl", + ) + parser.add_argument( + "--output_template", + type=str, + default="examples/research_cod/data/learn2ask/{split}.jsonl", + ) + parser.add_argument("--splits", nargs="+", default=["train", "test"]) + + args = parser.parse_args() + + for split in args.splits: + input_path = args.input_template.replace("{split}", split) + output_path = args.output_template.replace("{split}", split) + os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) + print(f"[{split}] {input_path} -> {output_path}") + main(input_path, output_path) diff --git a/examples/research_cod/exp_plan_learn2ask/data_prepare/llm_info_extraction.py b/examples/research_cod/exp_plan_learn2ask/data_prepare/llm_info_extraction.py new file mode 100644 index 00000000000..e218b06b083 --- /dev/null +++ b/examples/research_cod/exp_plan_learn2ask/data_prepare/llm_info_extraction.py @@ -0,0 +1,199 @@ +import ast +import os +import re +import traceback +from typing import List + +import openai + +llm = None + + +# System prompt shared by the single-shot and batched entry points. +_SYSTEM_MESSAGE = """ + # Task: + You are a medical information assistant. Given a dialogue between a physician (assistant) and a patient (user), extract the clinical attributes of interest to the physician based on their questions. The target fields include: symptom, symptom nature, symptom location, symptom severity, and symptom trigger. Then, identify the corresponding specific information from the patient's responses and pair it with the respective field. + # Requirements: + - Do not fabricate information or introduce new fields not listed above. Ignore patient-reported information regarding prior medication use, allergies, or underlying comorbidities; do not include such details in the output. + - Only include fields explicitly inquired about by the physician. Omit any fields not addressed in the dialogue. Avoid outputting vague terms (e.g., "unspecified" or "unknown"). + - Prevent duplication: if a symptom description already includes anatomical location, do not separately list the location field. + - Format each entry as a string enclosed in single quotes ('), separate multiple entries with commas, and enclose them in square brackets to form a list. Prefix the list with "output: " on the final line. If the dialogue is unrelated to the aforementioned clinical attributes, output "output: []". + - Do not include any commentary after the "output:" line. Condense colloquial patient expressions into concise, standardized, and clinically appropriate terminology. + # Example output format: + output: ['symptom: diarrhea', 'symptom nature: watery stool', 'symptom severity: 4-5 times per day'] + """ + + +def _build_messages(remaining_chat: str) -> list: + return [ + {"role": "system", "content": _SYSTEM_MESSAGE}, + {"role": "user", "content": "```\n" + remaining_chat + "\n```\n"}, + ] + + +def LLM_info_extraction(remaining_chat, model_call_mode, **kwargs): + """ + Extract information from a single remaining_chat using LLM. + + Kept for backward compatibility and ad-hoc debugging; the main + pipeline uses LLM_info_extraction_batch for throughput. + """ + messages = _build_messages(remaining_chat) + try: + if model_call_mode == "online_api": + return _call_online_api(messages, **kwargs) + elif model_call_mode == "local_vllm": + return _call_local_vllm(messages, **kwargs) + else: + return f"Error: Invalid model_call_mode '{model_call_mode}'. Must be 'online_api' or 'local_vllm'." + except Exception as e: + return f"Error occurred: {str(e)}" + + +def LLM_info_extraction_batch( + remaining_chats: List[str], model_call_mode: str, **kwargs +) -> List[str]: + """Batched counterpart of LLM_info_extraction. + + Builds the prompt for every remaining_chat, then issues a single + llm.generate(all_prompts) call so vLLM's continuous batching can + fully utilise the GPUs. Returns a list of response texts, one per + input chat, in the same order. + + online_api mode has no true batching (DashScope etc. are per-call), + so it just falls back to a sequential loop. + """ + messages_list = [_build_messages(rc) for rc in remaining_chats] + + if model_call_mode == "local_vllm": + return _call_local_vllm_batch(messages_list, **kwargs) + if model_call_mode == "online_api": + return [_call_online_api(m, **kwargs) for m in messages_list] + err = f"Error: Invalid model_call_mode '{model_call_mode}'. Must be 'online_api' or 'local_vllm'." + return [err] * len(remaining_chats) + + +def _call_online_api(messages, **kwargs): + """Handle OpenAI-style API calls""" + # Extract API parameters from kwargs or use defaults + api_key = kwargs.get("api_key", os.getenv("DASHSCOPE_API_KEY")) + api_base = kwargs.get("api_base", "https://dashscope.aliyuncs.com/compatible-mode/v1") + model = kwargs.get("model", "qwen2.5-72b-instruct") + temperature = kwargs.get("temperature", 0.7) + max_tokens = kwargs.get("max_tokens", 500) + + client = openai.OpenAI(api_key=api_key, base_url=api_base) + response = client.chat.completions.create( + model=model, messages=messages, temperature=temperature, max_tokens=max_tokens + ) + + return response.choices[0].message.content + + +def _vllm_setup(**kwargs): + """Lazy-init (and cache) the vLLM engine. Returns the global llm and + the SamplingParams to use.""" + from vllm import LLM, SamplingParams + + model_path = kwargs.get("model_path") + if not model_path: + raise ValueError("model_path is required for local vLLM inference") + + # Qwen3.6 thinking general, official: temp 1.0 / top_p 0.95 / top_k 20 / min_p 0 / + # presence_penalty 0. max_tokens 32768 per the card — needs room before "output:". + temperature = 1.0 + top_p = 0.95 + top_k = 20 + min_p = 0.0 + presence_penalty = 0.0 + max_tokens = 32768 + + tensor_parallel_size = kwargs.get("tensor_parallel_size", 1) + data_parallel_size = kwargs.get("data_parallel_size", 1) + gpu_memory_utilization = kwargs.get("gpu_memory_utilization", 0.9) + enforce_eager = kwargs.get("enforce_eager", False) + dtype = kwargs.get("dtype", "auto") + max_model_len = 40960 # 32768 output + headroom for the dialogue prompt + + global llm + if llm is None: + llm = LLM( + model=model_path, + tensor_parallel_size=tensor_parallel_size, + data_parallel_size=data_parallel_size, + gpu_memory_utilization=gpu_memory_utilization, + enforce_eager=enforce_eager, + dtype=dtype, + max_model_len=max_model_len, + ) + + sampling_params = SamplingParams( + temperature=temperature, + top_p=top_p, + top_k=top_k, + min_p=min_p, + presence_penalty=presence_penalty, + max_tokens=max_tokens, + ) + return llm, sampling_params + + +def _call_local_vllm(messages, **kwargs): + """Single-conversation local vLLM call via llm.chat: the engine renders + the chat template, and enable_thinking is passed via chat_template_kwargs.""" + try: + llm, sampling_params = _vllm_setup(**kwargs) + et = kwargs.get("enable_thinking") + ctk = {"enable_thinking": et} if et is not None else None + outputs = llm.chat(messages, sampling_params, chat_template_kwargs=ctk) + return outputs[0].outputs[0].text + except ImportError: + return "Error: vLLM library not installed. Please install it with 'pip install vllm'" + except Exception as e: + traceback.print_exc() # surface the real error instead of silently swallowing it + return f"Error in local vLLM inference: {str(e)}" + + +def _call_local_vllm_batch(messages_list: List[list], **kwargs) -> List[str]: + """Batched local vLLM call: hand all conversations to one llm.chat so + vLLM continuous-batches internally. Returns one output text per input, + in order. enable_thinking is passed via chat_template_kwargs. + """ + if not messages_list: + return [] + try: + llm, sampling_params = _vllm_setup(**kwargs) + et = kwargs.get("enable_thinking") + ctk = {"enable_thinking": et} if et is not None else None + outputs = llm.chat(messages_list, sampling_params, chat_template_kwargs=ctk) + return [o.outputs[0].text for o in outputs] + except ImportError: + return ["Error: vLLM library not installed. Please install it with 'pip install vllm'"] * len(messages_list) + except Exception as e: + traceback.print_exc() # surface the real error instead of silently swallowing it + return [f"Error in local vLLM inference: {str(e)}"] * len(messages_list) + + +def parse_llm_output(output_str): + """ + Convert the LLM info extraction output string to a list of strings. + + Args: + output_str (str): model output ending with "output: [...]" (a bracketed + list of "field: value" strings; "output: []" if nothing relevant). + + Returns: + list: List of strings if successful, error message string if failed + """ + # Lock onto the "output: [...]" format; take the LAST match so any + # reasoning or prose before it is ignored. + matches = re.findall(r"output:\s*(\[[^][]*\])", output_str, flags=re.DOTALL | re.IGNORECASE) + if not matches: + return f"Error parsing output: no 'output: [...]' found in [{repr(output_str)}]" + try: + result = ast.literal_eval(matches[-1]) + if not isinstance(result, list): + return f"Error: Expected a list, got {type(result)}" + return result + except Exception as e: + return f"Error parsing output: [{repr(matches[-1])}] error = {str(e)}" diff --git a/examples/research_cod/exp_plan_learn2ask/data_prepare/make_ood_split.py b/examples/research_cod/exp_plan_learn2ask/data_prepare/make_ood_split.py new file mode 100644 index 00000000000..2bddc0bb970 --- /dev/null +++ b/examples/research_cod/exp_plan_learn2ask/data_prepare/make_ood_split.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +"""Re-split learn2ask train+test into an OOD split by disease (diagn). + +Merges the existing train.jsonl + test.jsonl produced by +2_build_dataset.py, then routes whole diagn groups into the new +test split so train/test diagns are disjoint. Default picks diagns +greedily (random seeded order) until the cumulative sample count +reaches `--test_ratio * total`; pass `--test_diagns` to override +with an explicit list. + +Prints the diagn distribution at the start of every run so the +ratio target / explicit list can be sanity-checked against actual +data. +""" +import argparse +import json +import os +import random +from collections import Counter +from typing import Dict, List + + +def load_jsonl(path: str) -> List[dict]: + with open(path, "r", encoding="utf-8") as f: + return [json.loads(line) for line in f if line.strip()] + + +def write_jsonl(path: str, samples: List[dict]) -> None: + os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True) + with open(path, "w", encoding="utf-8") as f: + for s in samples: + f.write(json.dumps(s, ensure_ascii=False) + "\n") + + +def pick_test_diagns_by_ratio( + diagn_counts: Dict[str, int], + target_samples: int, + seed: int, +) -> List[str]: + """Greedy: shuffle diagns under `seed`, append until cumulative + sample count reaches the target. Overshoot up to one diagn is + accepted — use --test_diagns for finer control.""" + diagns = list(diagn_counts.keys()) + random.Random(seed).shuffle(diagns) + chosen, acc = [], 0 + for d in diagns: + if acc >= target_samples: + break + chosen.append(d) + acc += diagn_counts[d] + return chosen + + +def main(): + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument( + "--input_train", + default="examples/research_cod/data/learn2ask/train.jsonl", + ) + parser.add_argument( + "--input_test", + default="examples/research_cod/data/learn2ask/test.jsonl", + ) + parser.add_argument( + "--output_train", + default="examples/research_cod/data/learn2ask_ood/train.jsonl", + ) + parser.add_argument( + "--output_test", + default="examples/research_cod/data/learn2ask_ood/test.jsonl", + ) + parser.add_argument( + "--test_ratio", + type=float, + default=0.2, + help="Target fraction of samples routed to test, picked by diagn group.", + ) + parser.add_argument( + "--test_diagns", + type=str, + default=None, + help="Comma-separated explicit diagn list. Overrides --test_ratio.", + ) + parser.add_argument("--seed", type=int, default=42) + parser.add_argument( + "--show_dist_only", + action="store_true", + help="Print the diagn distribution and exit without writing splits.", + ) + args = parser.parse_args() + + train_in = load_jsonl(args.input_train) + test_in = load_jsonl(args.input_test) + samples = train_in + test_in + print( + f"Loaded {len(samples)} samples " + f"({len(train_in)} train + {len(test_in)} test) from {args.input_train} / {args.input_test}" + ) + + counts = Counter(s.get("diagn", "(missing)") for s in samples) + total = sum(counts.values()) + + print(f"\nDiagn distribution ({len(counts)} unique diagns):") + for d, n in counts.most_common(): + print(f" {n:6d} ({100 * n / total:5.2f}%) {d}") + + if args.show_dist_only: + return + + if args.test_diagns: + test_diagns = {d.strip() for d in args.test_diagns.split(",") if d.strip()} + unknown = test_diagns - set(counts) + if unknown: + raise ValueError(f"--test_diagns contains unknown diagns: {sorted(unknown)}") + else: + target = int(total * args.test_ratio) + test_diagns = set(pick_test_diagns_by_ratio(counts, target, args.seed)) + + train_samples = [s for s in samples if s.get("diagn") not in test_diagns] + test_samples = [s for s in samples if s.get("diagn") in test_diagns] + + write_jsonl(args.output_train, train_samples) + write_jsonl(args.output_test, test_samples) + + train_diagns = set(counts) - test_diagns + print("\nSplit summary:") + print( + f" train: {len(train_samples)} samples / {len(train_diagns)} diagns " + f"-> {args.output_train}" + ) + print( + f" test: {len(test_samples)} samples / {len(test_diagns)} diagns " + f"({100 * len(test_samples) / total:.2f}% of total) -> {args.output_test}" + ) + print(f"\nTest diagns ({len(test_diagns)}): {sorted(test_diagns)}") + + +if __name__ == "__main__": + main() diff --git a/examples/research_cod/get_alchemy_data.py b/examples/research_cod/get_alchemy_data.py new file mode 100644 index 00000000000..735597e465e --- /dev/null +++ b/examples/research_cod/get_alchemy_data.py @@ -0,0 +1,59 @@ +"""Generate Alchemy datasets with configurable size.""" + +import argparse +import os + +import numpy as np +import pandas as pd + +DEFAULT_DATA_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "data", "alchemy" +) + + +def save_dataset_to_local(data_path: str, data: list, split: str = "default") -> str: + os.makedirs(data_path, exist_ok=True) + data_df = pd.DataFrame(data) + dataset_path = os.path.join(data_path, f"{split}.parquet") + data_df.to_parquet(dataset_path) + print(f"Saved split '{split}' with {len(data)} examples at {dataset_path}") + return dataset_path + + +def prepare_alchemy_data(data_path: str, train_size: int, test_size: int, seed: int): + np.random.seed(seed) + + all_seeds = np.random.choice(10_000_000, size=train_size + test_size, replace=False) + train_seeds = all_seeds[:train_size] + test_seeds = all_seeds[train_size:] + + def process_fn(seed_val, idx): + return {"seed": int(seed_val), "index": idx, "uid": f"alchemy_{seed_val}"} + + train_data = [process_fn(s, i) for i, s in enumerate(train_seeds)] + test_data = [process_fn(s, i) for i, s in enumerate(test_seeds)] + + save_dataset_to_local(data_path, train_data, "train") + save_dataset_to_local(data_path, test_data, "test") + + return train_data, test_data + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Generate Alchemy dataset") + parser.add_argument("--local_dir", default=DEFAULT_DATA_PATH) + parser.add_argument("--train_size", type=int, default=50000) + parser.add_argument("--test_size", type=int, default=100) + parser.add_argument("--seed", type=int, default=42) + args = parser.parse_args() + + train_data, test_data = prepare_alchemy_data( + data_path=args.local_dir, + train_size=args.train_size, + test_size=args.test_size, + seed=args.seed, + ) + + print(f"\nTrain: {len(train_data)} examples") + print(f"Test: {len(test_data)} examples") + print(f"Sample: {train_data[0]}") diff --git a/examples/research_cod/get_frozen_lake_data.py b/examples/research_cod/get_frozen_lake_data.py new file mode 100644 index 00000000000..c21f9f0cb04 --- /dev/null +++ b/examples/research_cod/get_frozen_lake_data.py @@ -0,0 +1,85 @@ +""" +Generate FrozenLake datasets with configurable map size range. +Modified from examples/grpo_frozen_lake/get_frozen_lake_data.py +""" +import argparse +import os + +import numpy as np +import pandas as pd + +DEFAULT_DATA_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data", "frozen_lake") + + +def save_dataset_to_local(data_path: str, data: list[dict], split: str = "default") -> str: + os.makedirs(data_path, exist_ok=True) + data_df = pd.DataFrame(data) + dataset_path = os.path.join(data_path, f"{split}.parquet") + data_df.to_parquet(dataset_path) + print(f"Saved split '{split}' with {len(data)} examples at {dataset_path}") + return dataset_path + + +def prepare_frozenlake_data( + data_path, + train_size=10000, + test_size=100, + map_min_size=4, + map_max_size=8, + tile_min_prob=0.6, + tile_max_prob=0.85, + ): + np.random.seed(42) + + train_seeds = np.random.randint(0, 100000, size=train_size) + test_seeds = np.random.randint(0, 100000, size=test_size) + # randint is [low, high), so use max_size+1 to include max_size + train_sizes = np.random.randint(map_min_size, map_max_size + 1, size=train_size) + test_sizes = np.random.randint(map_min_size, map_max_size + 1, size=test_size) + # p is the probability of frozen tile, i.e., 1 - p is the probability of hole + train_ps = np.random.uniform(tile_min_prob, tile_max_prob, size=train_size) + test_ps = np.random.uniform(tile_min_prob, tile_max_prob, size=test_size) + + def frozenlake_process_fn(seed, size, p, idx): + return {"seed": seed, "size": size, "p": p, "index": idx, "uid": f"{seed}_{size}_{p}"} + + train_data = [ + frozenlake_process_fn(seed, train_sizes[idx], train_ps[idx], idx) + for idx, seed in enumerate(train_seeds) + ] + test_data = [ + frozenlake_process_fn(seed, test_sizes[idx], test_ps[idx], idx) + for idx, seed in enumerate(test_seeds) + ] + + save_dataset_to_local(data_path, train_data, "train") + save_dataset_to_local(data_path, test_data, "test") + + return train_data, test_data + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--local_dir", default=DEFAULT_DATA_PATH) + parser.add_argument("--train_size", type=int, default=50000) + parser.add_argument("--test_size", type=int, default=100) + parser.add_argument("--map_min_size", type=int, default=4) + parser.add_argument("--map_max_size", type=int, default=5) + parser.add_argument("--tile_min_prob", type=float, default=0.6) # tile prob: larger is easier + parser.add_argument("--tile_max_prob", type=float, default=0.7) + args = parser.parse_args() + + train_data, test_data = prepare_frozenlake_data( + data_path=args.local_dir, + train_size=args.train_size, + test_size=args.test_size, + map_min_size=args.map_min_size, + map_max_size=args.map_max_size, + tile_min_prob=args.tile_min_prob, + tile_max_prob=args.tile_max_prob, + ) + + print(f"\nTrain: {len(train_data)} examples") + print(f"Test: {len(test_data)} examples") + print(f"Size range: [{args.map_min_size}, {args.map_max_size}]") + print(f"Sample: {train_data[0]}") diff --git a/examples/research_cod/get_grid_navigation_data.py b/examples/research_cod/get_grid_navigation_data.py new file mode 100644 index 00000000000..08811890d37 --- /dev/null +++ b/examples/research_cod/get_grid_navigation_data.py @@ -0,0 +1,68 @@ +"""Generate seed-only datasets for the CoD grid-navigation environment.""" + +import argparse +import os + +import numpy as np +import pandas as pd + +DEFAULT_DATA_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "data", "grid_navigation" +) + + +def save_dataset_to_local(data_path: str, data: list[dict], split: str) -> str: + """Write one dataset split as a parquet file.""" + os.makedirs(data_path, exist_ok=True) + dataset_path = os.path.join(data_path, f"{split}.parquet") + pd.DataFrame(data).to_parquet(dataset_path) + print(f"Saved split '{split}' with {len(data)} examples at {dataset_path}") + return dataset_path + + +def prepare_grid_navigation_data( + data_path: str, + train_size: int, + test_size: int, + seed: int, +): + """Generate disjoint train and test task seeds and save both splits.""" + rng = np.random.default_rng(seed) + all_seeds = rng.choice(10_000_000, size=train_size + test_size, replace=False) + train_seeds = all_seeds[:train_size] + test_seeds = all_seeds[train_size:] + + def process_fn(task_seed: int, index: int) -> dict: + task_seed = int(task_seed) + return { + "seed": task_seed, + "index": index, + "uid": f"grid_navigation_{task_seed}", + } + + train_data = [process_fn(task_seed, i) for i, task_seed in enumerate(train_seeds)] + test_data = [process_fn(task_seed, i) for i, task_seed in enumerate(test_seeds)] + + save_dataset_to_local(data_path, train_data, "train") + save_dataset_to_local(data_path, test_data, "test") + return train_data, test_data + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Generate CoD grid-navigation data") + parser.add_argument("--local_dir", default=DEFAULT_DATA_PATH) + parser.add_argument("--train_size", type=int, default=50000) + parser.add_argument("--test_size", type=int, default=4000) + parser.add_argument("--seed", type=int, default=42) + args = parser.parse_args() + + train_data, test_data = prepare_grid_navigation_data( + data_path=args.local_dir, + train_size=args.train_size, + test_size=args.test_size, + seed=args.seed, + ) + + print(f"\nTrain: {len(train_data)} examples") + print(f"Test: {len(test_data)} examples") + print(f"Sample: {train_data[0]}") diff --git a/examples/research_cod/get_optimal_control_data.py b/examples/research_cod/get_optimal_control_data.py new file mode 100644 index 00000000000..aa34e68fffb --- /dev/null +++ b/examples/research_cod/get_optimal_control_data.py @@ -0,0 +1,231 @@ +"""Generate train/test datasets for the CoD Optimal Control workflow. + +The script supports reproducible random task generation with configurable +difficulty. A YAML config (e.g. the benchmark YAML) can be passed via +``--config``; any CLI arguments override the config values. +""" + +import argparse +import os +from typing import Any, Dict, List, Optional, Tuple + +import numpy as np +import pandas as pd +from omegaconf import OmegaConf + +from trinity.common.constants import TASKSET_PATH_ENV_VAR + +DEFAULT_DATA_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "..", "data", "optimal_control" +) + +# Difficulty presets control the horizon and sampled state space. The hard +# preset keeps state scales bounded while moving targets farther away. +DIFFICULTY_PRESETS: Dict[str, Dict[str, Any]] = { + "easy": { + "max_horizon": 8, + "x0_min": -1.0, + "x0_max": 1.0, + "v0_min": -0.5, + "v0_max": 0.5, + "x_target_min": -3.0, + "x_target_max": 3.0, + }, + "medium": { + "max_horizon": 12, + "x0_min": -2.0, + "x0_max": 2.0, + "v0_min": -1.0, + "v0_max": 1.0, + "x_target_min": -5.0, + "x_target_max": 5.0, + }, + "hard": { + "max_horizon": 12, + "x0_min": -2.0, + "x0_max": 2.0, + "v0_min": -1.5, + "v0_max": 1.5, + "x_target_min": -8.0, + "x_target_max": 8.0, + }, +} + + +def save_dataset_to_local(data_path: str, data: list[dict], split: str = "default") -> str: + """Save dataset directly to local data_path.""" + os.makedirs(data_path, exist_ok=True) + data_df = pd.DataFrame(data) + dataset_path = os.path.join(data_path, f"{split}.parquet") + data_df.to_parquet(dataset_path) + print( + f"Saved dataset optimal_control split '{split}' with {len(data)} examples at " + f"{dataset_path}. Make sure to set the environment variable {TASKSET_PATH_ENV_VAR} " + f"to {data_path}." + ) + return dataset_path + + +def _sample_float(rng: np.random.Generator, low: float, high: float) -> float: + """Sample a single float in [low, high).""" + return float(rng.uniform(low, high)) + + +def _difficulty_params( + difficulty: str, + overrides: Optional[Dict[str, Any]] = None, +) -> Dict[str, Any]: + """Resolve difficulty preset and apply per-field overrides.""" + if difficulty not in DIFFICULTY_PRESETS: + raise ValueError( + f"Unknown difficulty '{difficulty}'. Choose from {list(DIFFICULTY_PRESETS.keys())}." + ) + params = dict(DIFFICULTY_PRESETS[difficulty]) + # The control penalty is independent of difficulty by default. + params.setdefault("control_penalty_coef", 0.03) + params.setdefault("v_target", 0.0) + # The workflow uses max_horizon directly as the rollout horizon. + params.setdefault("max_horizon", 30) + if overrides: + for key, value in overrides.items(): + if value is not None and key in params: + params[key] = value + return params + + +def _generate_random_tasks( + rng: np.random.Generator, + size: int, + params: Dict[str, Any], + seed_offset: int, +) -> List[dict]: + """Generate independent tasks whose hidden dynamics are resolved at runtime.""" + return [ + { + "x0": _sample_float(rng, params["x0_min"], params["x0_max"]), + "v0": _sample_float(rng, params["v0_min"], params["v0_max"]), + "x_target": _sample_float( + rng, + params["x_target_min"], + params["x_target_max"], + ), + "v_target": float(params["v_target"]), + "max_horizon": int(params["max_horizon"]), + "control_penalty_coef": float(params["control_penalty_coef"]), + "seed": seed_offset + task_idx, + } + for task_idx in range(size) + ] + + +def prepare_optimal_control_data( + data_path: str, + train_size: int = 128, + test_size: int = 16, + difficulty: str = "medium", + train_seed: int = 42, + test_seed: int = 2024, + **range_overrides: Any, +) -> Tuple[List[dict], List[dict]]: + """Generate pack-independent train and test splits.""" + params = _difficulty_params(difficulty, range_overrides) + train_data = _generate_random_tasks( + np.random.default_rng(train_seed), + train_size, + params, + seed_offset=0, + ) + test_data = _generate_random_tasks( + np.random.default_rng(test_seed), + test_size, + params, + seed_offset=train_size, + ) + save_dataset_to_local(data_path, train_data, "train") + save_dataset_to_local(data_path, test_data, "test") + return train_data, test_data + + +def _load_config(config_path: Optional[str]) -> Dict[str, Any]: + """Load an optional YAML config and extract ``task_generation`` settings.""" + if not config_path: + return {} + cfg = OmegaConf.load(config_path) + task_gen = cfg.get("task_generation", {}) + if not task_gen: + return {} + return OmegaConf.to_container(task_gen, resolve=True) + + +def _build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser() + parser.add_argument("--local_dir", default=DEFAULT_DATA_PATH) + parser.add_argument( + "--config", default=None, help="Path to a YAML config with a 'task_generation' section." + ) + parser.add_argument("--train_size", type=int, default=None) + parser.add_argument("--test_size", type=int, default=None) + parser.add_argument("--difficulty", type=str, default=None) + parser.add_argument("--train_seed", type=int, default=None) + parser.add_argument("--test_seed", type=int, default=None) + parser.add_argument("--max_horizon", type=int, default=None) + parser.add_argument("--control_penalty_coef", type=float, default=None) + parser.add_argument("--x0_min", type=float, default=None) + parser.add_argument("--x0_max", type=float, default=None) + parser.add_argument("--v0_min", type=float, default=None) + parser.add_argument("--v0_max", type=float, default=None) + parser.add_argument("--x_target_min", type=float, default=None) + parser.add_argument("--x_target_max", type=float, default=None) + return parser + + +def main() -> None: + parser = _build_parser() + args = parser.parse_args() + + # Config values are the base; CLI args override them. + config = _load_config(args.config) + + def get(name: str, default: Any) -> Any: + cli_value = getattr(args, name, None) + if cli_value is not None: + return cli_value + return config.get(name, default) + + train_size = int(get("train_size", 128)) + test_size = int(get("test_size", 16)) + difficulty = str(get("difficulty", "medium")) + train_seed = int(get("train_seed", 42)) + test_seed = int(get("test_seed", 2024)) + + range_overrides = { + "max_horizon": get("max_horizon", None), + "control_penalty_coef": get("control_penalty_coef", None), + "x0_min": get("x0_min", None), + "x0_max": get("x0_max", None), + "v0_min": get("v0_min", None), + "v0_max": get("v0_max", None), + "x_target_min": get("x_target_min", None), + "x_target_max": get("x_target_max", None), + } + # Filter out unset overrides so the difficulty preset is not overwritten. + range_overrides = {k: v for k, v in range_overrides.items() if v is not None} + + train_data, test_data = prepare_optimal_control_data( + data_path=args.local_dir, + train_size=train_size, + test_size=test_size, + difficulty=difficulty, + train_seed=train_seed, + test_seed=test_seed, + **range_overrides, + ) + + print(f"Train dataset: {len(train_data)} examples") + print(f"Test dataset: {len(test_data)} examples") + print("Sample train example:", train_data[0]) + print("Sample test example:", test_data[0]) + + +if __name__ == "__main__": + main() diff --git a/examples/research_cod/get_pde_discovery_data.py b/examples/research_cod/get_pde_discovery_data.py new file mode 100644 index 00000000000..33f5bfcaae8 --- /dev/null +++ b/examples/research_cod/get_pde_discovery_data.py @@ -0,0 +1,513 @@ +"""Generate CoD PDE discovery tasksets. + +The dataset intentionally does not pre-bind rows to CoD packs. Pack-level +randomness is injected at runtime by CoDWorkflow via ``pack_seed`` so changing +pack size or mixing tasksets does not require regenerating the dataset. +""" + +import argparse +import importlib.util +import os +import sys +import types +from collections import Counter +from pathlib import Path +from typing import Iterable + +import numpy as np +import pandas as pd + +DEFAULT_DATA_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "data", "pde_discovery" +) + + +def save_dataset_to_local(data_path: str, data: list[dict], split: str) -> str: + os.makedirs(data_path, exist_ok=True) + dataset_path = os.path.join(data_path, f"{split}.parquet") + pd.DataFrame(data).to_parquet(dataset_path) + print(f"Saved split '{split}' with {len(data)} examples at {dataset_path}") + return dataset_path + + +def prepare_pde_discovery_data( + data_path: str, + train_size: int, + test_size: int, + seed: int, + eval_pack_size: int | None = None, + eval_template_count: int | None = None, + exclude_eval_paths: list[str] | None = None, + eval_ground_truth_family: str = "physical_full_eval_hard.json", + eval_min_reaction_terms: int = 1, + eval_max_reaction_terms: int = 3, + eval_state_abs_limit: float = 3.0, + eval_initial_mode_count_range: tuple[int, int] = (1, 2), + save_train_split: bool = True, + online_eval_data_path: str | None = None, + online_eval_instances_per_template: int | None = None, +) -> tuple[list[dict], list[dict]]: + counts = { + "train_size": train_size, + "test_size": test_size, + } + invalid = [name for name, value in counts.items() if value <= 0] + if invalid: + raise ValueError(f"PDE dataset counts must be positive: {', '.join(invalid)}") + if (online_eval_data_path is None) != (online_eval_instances_per_template is None): + raise ValueError( + "online_eval_data_path and online_eval_instances_per_template " + "must be provided together" + ) + if online_eval_data_path is not None: + if os.path.abspath(online_eval_data_path) == os.path.abspath(data_path): + raise ValueError( + "Online eval directory must differ from the full dataset directory" + ) + stale_online_train_path = os.path.join(online_eval_data_path, "train.parquet") + if os.path.exists(stale_online_train_path): + raise ValueError( + "Online test-only directory contains train.parquet: " + f"{stale_online_train_path}" + ) + + rng = np.random.default_rng(seed) + uint32_space_size = int(np.iinfo(np.uint32).max) + 1 + all_task_seeds = rng.choice( + uint32_space_size, + size=train_size + test_size, + replace=False, + ) + + eval_template_assignments = build_stratified_eval_template_assignments( + test_size=test_size, + eval_pack_size=eval_pack_size, + eval_template_count=eval_template_count, + ) + + def build_split(task_seeds, split_name, template_assignments=None): + rows = [] + for task_idx, task_seed in enumerate(task_seeds): + row = { + "uid": f"pde_{split_name}_{task_idx}", + "seed": int(task_seed), + "task_desc": ( + "Discover the nonlinear reaction term f(u) in " + "partial_t u = partial_xx u + f(u) using active " + "sampling, sparse regression, and scientific context." + ), + "answer": "", + } + if template_assignments is not None: + assignment = template_assignments[task_idx] + row.update(assignment) + pack_start = (task_idx // eval_pack_size) * eval_pack_size + row["pde_environment_seed"] = int(task_seeds[pack_start]) + rows.append(row) + return rows + + train_data = build_split( + all_task_seeds[:train_size], + "train", + ) + test_data = build_split( + all_task_seeds[train_size:], + "test", + template_assignments=eval_template_assignments, + ) + if exclude_eval_paths: + if eval_pack_size is None or eval_template_count is None: + raise ValueError( + "Equation-disjoint evaluation requires eval_pack_size and " + "eval_template_count" + ) + validate_eval_equation_disjointness( + new_rows=test_data, + exclude_eval_paths=exclude_eval_paths, + eval_pack_size=eval_pack_size, + ground_truth_family=eval_ground_truth_family, + min_reaction_terms=eval_min_reaction_terms, + max_reaction_terms=eval_max_reaction_terms, + state_abs_limit=eval_state_abs_limit, + initial_mode_count_range=eval_initial_mode_count_range, + ) + + if save_train_split: + save_dataset_to_local(data_path, train_data, "train") + else: + stale_train_path = os.path.join(data_path, "train.parquet") + if os.path.exists(stale_train_path): + raise ValueError( + f"--test_only requires a directory without train.parquet: {stale_train_path}" + ) + save_dataset_to_local(data_path, test_data, "test") + if online_eval_data_path is not None: + assert online_eval_instances_per_template is not None + if eval_pack_size is None or eval_template_count is None: + raise ValueError( + "Online eval export requires eval_pack_size and eval_template_count" + ) + online_test_data = select_eval_template_instances( + test_data, + eval_pack_size=eval_pack_size, + eval_template_count=eval_template_count, + instances_per_template=online_eval_instances_per_template, + ) + save_dataset_to_local(online_eval_data_path, online_test_data, "test") + return train_data, test_data + + +def _resolve_test_parquet(path: str) -> Path: + candidate = Path(path) + if candidate.is_dir(): + candidate = candidate / "test.parquet" + if not candidate.is_file(): + raise FileNotFoundError(f"Excluded eval test parquet does not exist: {candidate}") + return candidate + + +def _eval_pack_descriptors(rows: Iterable[dict], eval_pack_size: int) -> list[dict]: + """Return one hidden-environment descriptor for every fixed eval pack.""" + grouped: dict[int, list[dict]] = {} + for row in rows: + if "eval_pack_index" not in row: + raise ValueError("Stratified eval row is missing eval_pack_index") + grouped.setdefault(int(row["eval_pack_index"]), []).append(row) + + descriptors = [] + for pack_index in sorted(grouped): + pack = grouped[pack_index] + if len(pack) != eval_pack_size: + raise ValueError( + f"Eval pack {pack_index} has {len(pack)} rows, expected {eval_pack_size}" + ) + template_indices = {int(row["ground_truth_template_index"]) for row in pack} + environment_seeds = {int(row["pde_environment_seed"]) for row in pack} + if len(template_indices) != 1 or len(environment_seeds) != 1: + raise ValueError( + f"Eval pack {pack_index} does not share one template and environment seed" + ) + descriptors.append( + { + "pack_index": pack_index, + "template_index": template_indices.pop(), + "environment_seed": environment_seeds.pop(), + } + ) + return descriptors + + +def select_eval_template_instances( + rows: list[dict], + eval_pack_size: int, + eval_template_count: int, + instances_per_template: int, +) -> list[dict]: + """Select the first fixed equation instances of every eval template.""" + if instances_per_template <= 0: + raise ValueError("Online eval instances per template must be positive") + selected = [ + row + for row in rows + if int(row["ground_truth_template_instance"]) < instances_per_template + ] + descriptors = _eval_pack_descriptors(selected, eval_pack_size) + template_counts = Counter(descriptor["template_index"] for descriptor in descriptors) + expected_pack_count = eval_template_count * instances_per_template + if len(descriptors) != expected_pack_count: + raise ValueError( + f"Online eval selected {len(descriptors)} packs, expected {expected_pack_count}" + ) + if set(template_counts) != set(range(eval_template_count)) or set( + template_counts.values() + ) != {instances_per_template}: + raise ValueError("Online eval does not cover every template equally") + return selected + + +def _load_pde_ground_truth_modules(): + """Load equation-sampling modules without importing the vLLM workflow.""" + root = ( + Path(__file__).parents[2] + / "trinity/common/workflows/connect_the_dots/pde_discovery" + ) + package_name = "_pde_eval_generation_modules" + package = sys.modules.setdefault(package_name, types.ModuleType(package_name)) + package.__path__ = [str(root)] + + loaded = {} + for name in ("candidate", "pde_numeric", "ground_truth"): + module_name = f"{package_name}.{name}" + if module_name in sys.modules: + loaded[name] = sys.modules[module_name] + continue + spec = importlib.util.spec_from_file_location(module_name, root / f"{name}.py") + if spec is None or spec.loader is None: + raise ImportError(f"Cannot load PDE equation module: {name}") + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + loaded[name] = module + return loaded["candidate"], loaded["ground_truth"], loaded["pde_numeric"] + + +def _hidden_equation_signatures( + rows: Iterable[dict], + eval_pack_size: int, + ground_truth_family: str, + min_reaction_terms: int, + max_reaction_terms: int, + state_abs_limit: float, + initial_mode_count_range: tuple[int, int], +) -> list[tuple[tuple[tuple[str, float], ...], int]]: + """Reproduce the workflow's stable hidden equation for each eval pack. + + The equation signature is support plus rounded coefficients. Environment + seeds and initial conditions are deliberately not part of the signature, so + two packs with the same mathematical reaction are treated as duplicates. + """ + candidate, ground_truth, pde_numeric = _load_pde_ground_truth_modules() + + templates = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family=ground_truth_family, + ) + initial_shape = pde_numeric.InitialConditionShapeConfig.from_mapping( + {"mode_count_range": list(initial_mode_count_range)} + ) + initial_amplitude_upper = ground_truth.calibrate_family_initial_amplitude_upper( + templates, + min_reaction_terms, + max_reaction_terms, + state_abs_limit=state_abs_limit, + ) + + signatures = [] + for descriptor in _eval_pack_descriptors(rows, eval_pack_size): + environment_seed = descriptor["environment_seed"] + rng = np.random.default_rng( + np.random.SeedSequence( + [environment_seed, pde_numeric.GROUND_TRUTH_STREAM] + ) + ) + coefficients = {} + for _ in range(ground_truth.GT_STABILITY_MAX_ATTEMPTS): + _, coefficients = ground_truth.sample_hidden_reaction( + rng, + templates, + min_reaction_terms, + max_reaction_terms, + template_index=descriptor["template_index"], + ) + if ground_truth.is_stable_reaction_candidate( + coefficients=coefficients, + pde_grid_size=257, + pde_time_steps=2001, + trajectory_count=eval_pack_size, + pack_seed=environment_seed, + initial_amplitude_upper=initial_amplitude_upper, + initial_condition_shape=initial_shape, + state_abs_limit=state_abs_limit, + ): + break + signature = tuple( + (term, float(coefficients[term])) + for term in candidate.DEFAULT_DICTIONARY + if term in coefficients + ) + signatures.append((signature, descriptor["pack_index"])) + return signatures + + +def _format_equation_signature(signature: tuple[tuple[str, float], ...]) -> str: + return " + ".join(f"{coefficient:+.2f}*{term}" for term, coefficient in signature) + + +def validate_eval_equation_disjointness( + new_rows: list[dict], + exclude_eval_paths: list[str], + eval_pack_size: int, + ground_truth_family: str, + min_reaction_terms: int, + max_reaction_terms: int, + state_abs_limit: float, + initial_mode_count_range: tuple[int, int], +) -> None: + """Fail generation when exact hidden equations repeat internally or historically.""" + signature_args = { + "eval_pack_size": eval_pack_size, + "ground_truth_family": ground_truth_family, + "min_reaction_terms": min_reaction_terms, + "max_reaction_terms": max_reaction_terms, + "state_abs_limit": state_abs_limit, + "initial_mode_count_range": initial_mode_count_range, + } + new_records = _hidden_equation_signatures(new_rows, **signature_args) + new_counts = Counter(signature for signature, _ in new_records) + internal_duplicates = [signature for signature, count in new_counts.items() if count > 1] + if internal_duplicates: + rendered = "; ".join( + _format_equation_signature(signature) for signature in internal_duplicates[:5] + ) + raise ValueError(f"New eval set contains duplicate hidden equations: {rendered}") + + excluded_signatures = set() + excluded_pack_count = 0 + for excluded_path in exclude_eval_paths: + parquet_path = _resolve_test_parquet(excluded_path) + old_rows = pd.read_parquet(parquet_path).to_dict("records") + old_records = _hidden_equation_signatures(old_rows, **signature_args) + excluded_signatures.update(signature for signature, _ in old_records) + excluded_pack_count += len(old_records) + + overlaps = sorted(set(new_counts) & excluded_signatures, key=repr) + if overlaps: + rendered = "; ".join( + _format_equation_signature(signature) for signature in overlaps[:5] + ) + raise ValueError( + f"New eval set overlaps {len(overlaps)} excluded hidden equations: {rendered}" + ) + print( + "Verified hidden-equation disjointness: " + f"{len(new_records)} new packs are unique and disjoint from " + f"{excluded_pack_count} excluded packs" + ) + + +def build_stratified_eval_template_assignments( + test_size: int, + eval_pack_size: int | None, + eval_template_count: int | None, +) -> list[dict] | None: + """Assign an equal number of fixed eval packs to every template. + + Training rows remain pack-agnostic. This optional metadata is intended only + for a fixed scientific benchmark whose row order and pack size are held + constant across checkpoints. The metadata is consumed inside the PDE + environment and is never rendered into the model prompt. + """ + if eval_pack_size is None and eval_template_count is None: + return None + if eval_pack_size is None or eval_template_count is None: + raise ValueError( + "eval_pack_size and eval_template_count must be provided together" + ) + if eval_pack_size <= 0 or eval_template_count <= 0: + raise ValueError("eval pack size and template count must be positive") + if test_size % eval_pack_size != 0: + raise ValueError( + f"test_size={test_size} must be divisible by eval_pack_size={eval_pack_size}" + ) + + pack_count = test_size // eval_pack_size + if pack_count % eval_template_count != 0: + raise ValueError( + f"eval pack count {pack_count} must be divisible by " + f"eval_template_count={eval_template_count} for balanced coverage" + ) + + assignments = [] + for pack_index in range(pack_count): + template_index = pack_index % eval_template_count + template_instance_index = pack_index // eval_template_count + assignment = { + "eval_pack_index": pack_index, + "ground_truth_template_index": template_index, + "ground_truth_template_instance": template_instance_index, + } + assignments.extend(dict(assignment) for _ in range(eval_pack_size)) + return assignments + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Generate PDE discovery CoD dataset") + parser.add_argument("--local_dir", default=DEFAULT_DATA_PATH) + parser.add_argument("--train_size", type=int, default=50000) + parser.add_argument("--test_size", type=int, default=32) + parser.add_argument("--seed", type=int, default=42) + parser.add_argument( + "--eval_pack_size", + type=int, + default=None, + help="Fixed CoD pack size for an optional stratified test split.", + ) + parser.add_argument( + "--eval_template_count", + type=int, + default=None, + help=( + "Number of hidden templates to cover equally in the stratified test " + "split. Must be used with --eval_pack_size." + ), + ) + parser.add_argument( + "--exclude_eval_path", + action="append", + default=[], + help=( + "Existing stratified eval directory or test.parquet whose exact hidden " + "equations must not occur in the new test split. May be repeated." + ), + ) + parser.add_argument( + "--online_eval_dir", + default=None, + help=( + "Optional directory for a smaller, deterministic online-eval subset " + "selected from the full stratified test split." + ), + ) + parser.add_argument( + "--online_eval_instances_per_template", + type=int, + default=None, + help=( + "Number of the first equation instances to retain per hidden template " + "in --online_eval_dir. Must be used with --online_eval_dir." + ), + ) + parser.add_argument( + "--eval_ground_truth_family", + default="physical_full_eval_hard.json", + help="Ground-truth family used to reproduce equations for overlap checks.", + ) + parser.add_argument("--eval_min_reaction_terms", type=int, default=1) + parser.add_argument("--eval_max_reaction_terms", type=int, default=3) + parser.add_argument("--eval_state_abs_limit", type=float, default=3.0) + parser.add_argument( + "--eval_initial_mode_count_range", + type=int, + nargs=2, + metavar=("MIN", "MAX"), + default=(1, 2), + ) + parser.add_argument( + "--test_only", + action="store_true", + help="Write only test.parquet, avoiding mixed-schema dataset directories.", + ) + args = parser.parse_args() + + train_data, test_data = prepare_pde_discovery_data( + data_path=args.local_dir, + train_size=args.train_size, + test_size=args.test_size, + seed=args.seed, + eval_pack_size=args.eval_pack_size, + eval_template_count=args.eval_template_count, + exclude_eval_paths=args.exclude_eval_path, + eval_ground_truth_family=args.eval_ground_truth_family, + eval_min_reaction_terms=args.eval_min_reaction_terms, + eval_max_reaction_terms=args.eval_max_reaction_terms, + eval_state_abs_limit=args.eval_state_abs_limit, + eval_initial_mode_count_range=tuple(args.eval_initial_mode_count_range), + save_train_split=not args.test_only, + online_eval_data_path=args.online_eval_dir, + online_eval_instances_per_template=args.online_eval_instances_per_template, + ) + + print(f"\nTrain rows: {len(train_data)}") + print(f"Test rows: {len(test_data)}") + if train_data: + print(f"Sample: {train_data[0]}") diff --git a/examples/research_cod/get_terminal_data.py b/examples/research_cod/get_terminal_data.py new file mode 100644 index 00000000000..cef70f3ad9e --- /dev/null +++ b/examples/research_cod/get_terminal_data.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +"""Generate training and evaluation datasets for the terminal file-ops task. + +Usage:: + + python examples/research_cod/get_terminal_data.py \ + --local_dir examples/research_cod/data/terminal \ + --train_size 50000 \ + --test_size 200 +""" + +import argparse +import os +import random + +import numpy as np +import pandas as pd + + +DEFAULT_DATA_PATH = "examples/research_cod/data/terminal" + +# Task types that can appear in the dataset +SINGLE_TYPES = [ + "upload", "download", "rename", "move", "chmod", + "delete", "copy", "pack", "mkdir", +] +COMPOSITE_TYPES = [ + "pack_upload", "download_extract", "mkdir_upload", + "upload_chmod", "upload_delete_source", "pack_upload_extract", + "download_rename", "backup_replace", +] +ALL_TYPES = SINGLE_TYPES + COMPOSITE_TYPES + + +def save_dataset_to_local(data_path: str, data: list, split: str) -> str: + os.makedirs(data_path, exist_ok=True) + df = pd.DataFrame(data) + path = os.path.join(data_path, f"{split}.parquet") + df.to_parquet(path) + print(f"Saved {len(data)} samples to {path}") + return path + + +def prepare_data( + data_path: str, + train_size: int = 50000, + test_size: int = 200, + seed: int = 42, + composite_ratio: float = 0.5, +): + np.random.seed(seed) + + # Generate unique seeds + all_seeds = set() + while len(all_seeds) < train_size + test_size: + all_seeds.add(np.random.randint(0, 10_000_000)) + all_seeds = list(all_seeds) + np.random.shuffle(all_seeds) + + train_seeds = all_seeds[:train_size] + test_seeds = all_seeds[train_size:train_size + test_size] + + def make_record(task_seed, idx): + # The task type is determined by the seed at runtime; + # but we precompute it here for metadata.source (pack grouping). + task_rng = random.Random(task_seed) + task_rng.choice(["windows", "mac", "linux"]) # skip OS pick + task_rng.randint(10, 99) # skip remote host + task_rng.choice(["admin", "deploy", "user", "webmaster", "devops", "ops", "ubuntu"]) # skip user + is_composite = task_rng.random() < composite_ratio + if is_composite: + task_type = task_rng.choice(COMPOSITE_TYPES) + else: + task_type = task_rng.choice(SINGLE_TYPES) + + return { + "seed": int(task_seed), + "prompt": str(task_seed), + "index": idx, + "uid": f"terminal_{task_seed}", + "metadata": { + "source": task_type, + "source_dataset": "terminal", + }, + } + + train_data = [make_record(s, i) for i, s in enumerate(train_seeds)] + test_data = [make_record(s, i) for i, s in enumerate(test_seeds)] + + save_dataset_to_local(data_path, train_data, "train") + save_dataset_to_local(data_path, test_data, "test") + + # Print distribution + from collections import Counter + train_types = Counter(r["metadata"]["source"] for r in train_data) + print(f"\nTask type distribution (train):") + for t, c in sorted(train_types.items(), key=lambda x: -x[1]): + print(f" {t}: {c} ({100 * c / train_size:.1f}%)") + + return train_data, test_data + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Generate terminal task dataset") + parser.add_argument("--local_dir", default=DEFAULT_DATA_PATH) + parser.add_argument("--train_size", type=int, default=50000) + parser.add_argument("--test_size", type=int, default=200) + parser.add_argument("--seed", type=int, default=42) + parser.add_argument("--composite_ratio", type=float, default=0.5) + args = parser.parse_args() + + prepare_data( + data_path=args.local_dir, + train_size=args.train_size, + test_size=args.test_size, + seed=args.seed, + composite_ratio=args.composite_ratio, + ) diff --git a/pyproject.toml b/pyproject.toml index 008c00ab23f..a2469eb7264 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ dependencies = [ "datasets>=4.0.0", "typer>=0.23.0", "fsspec>=2023.10.0", + "gymnasium", ] [project.scripts] @@ -58,7 +59,7 @@ trinity = "trinity.cli.launcher:main" [project.optional-dependencies] vllm = [ # for routed_experts support, please install vllm 0.22.0 or above - "vllm>=0.19.1,<=0.23.0", + "vllm>=0.22.0,<=0.23.0", ] sglang = [ "sglang==0.5.13", @@ -127,7 +128,7 @@ flash_attn = [ qwen3_5 = [ "flash-linear-attention>=0.4.2", - "causal_conv1d>=1.6.0", + "causal_conv1d>=1.6.2.post1", ] [tool.setuptools.packages.find] diff --git a/tests/common/pde_discovery_test.py b/tests/common/pde_discovery_test.py new file mode 100644 index 00000000000..8fb0ccc9ca2 --- /dev/null +++ b/tests/common/pde_discovery_test.py @@ -0,0 +1,574 @@ +import importlib.util +import sys +import types +from collections import Counter +from pathlib import Path + +import numpy as np + +PDE_ROOT = ( + Path(__file__).parents[2] + / "trinity/common/workflows/connect_the_dots/pde_discovery" +) +PACKAGE = "pde_discovery_test_modules" + + +def _load_module(name: str, path: Path | None = None): + package = sys.modules.setdefault(PACKAGE, types.ModuleType(PACKAGE)) + package.__path__ = [str(PDE_ROOT)] + module_name = f"{PACKAGE}.{name}" + spec = importlib.util.spec_from_file_location( + module_name, path or PDE_ROOT / f"{name}.py" + ) + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + return module + + +candidate = _load_module("candidate") +pde_numeric = _load_module("pde_numeric") +ground_truth = _load_module("ground_truth") +regression = _load_module("regression") +cod_utils = _load_module("cod_utils", PDE_ROOT.parent / "utils.py") +prompts = _load_module("prompts", PDE_ROOT / "prompts/__init__.py") +data_generator = _load_module( + "data_generator", + Path(__file__).parents[2] / "examples/research_cod/get_pde_discovery_data.py", +) + +INITIAL_CONDITION_TEST_PACK_SEED = 1234 +INITIAL_CONDITION_TEST_GRID_SIZE = 129 +INITIAL_CONDITION_TEST_TRAJECTORY_COUNTS = (1, 3, 5, 8) +INITIAL_CONDITION_TEST_STATE_ABS_LIMIT = 3.0 +INITIAL_CONDITION_TEST_SHAPE = pde_numeric.InitialConditionShapeConfig( + mode_count_range=(1, 4), +) + + +def _parse_action(response: str): + payload, parse_error = cod_utils.parse_xml_answer( + response, {"dictionary", "candidate_equations", "uncertain_terms"} + ) + return types.SimpleNamespace(payload=payload, parse_error=parse_error or None) + + +def test_xml_answer_protocol_parses_reasoning_and_sampling(): + response = """I should sample broad regions first. + + + + + + + + + Additional reasoning outside the answer is ignored. + """ + + result = _parse_action(response) + + assert result.parse_error is None + assert result.payload == { + "action": "sample_pde_data", + "args": { + "point_grid": { + "point": [ + {"x": "0.2", "t": "0.1"}, + {"x": "0.8", "t": "0.9"}, + ], + } + }, + } + + +def test_xml_answer_protocol_parses_all_pde_tools(): + cases = [ + ( + "", + {"action": "summarize_pack_evidence", "args": {}}, + ), + ( + """ + merged_all0.05 + uu**3 + """, + { + "action": "run_sparse_regression", + "args": { + "dataset_id": "merged_all", + "alpha": "0.05", + "dictionary": ["u", "u**3"], + }, + }, + ), + ( + """ + 1.2*u - 0.8*u**3 + Cubic support is preferred. + sin(u) + """, + { + "action": "update_scientific_context", + "args": { + "preferred_equation": "1.2*u - 0.8*u**3", + "note": "Cubic support is preferred.", + "uncertain_terms": ["sin(u)"], + }, + }, + ), + ] + + for response, expected in cases: + result = _parse_action(response) + assert result.parse_error is None + assert result.payload == expected + + +def test_xml_answer_protocol_parses_candidate_list(): + equations = "".join(f"{idx}*u" for idx in range(8)) + response = ( + "" + f"{equations}" + ) + + result = _parse_action(response) + + assert result.parse_error is None + assert len(result.payload["args"]["candidate_equations"]) == 8 + + +def test_xml_answer_protocol_rejects_ambiguous_or_invalid_actions(): + cases = [ + ( + "" + "", + "expected_exactly_one_answer_tag", + ), + ( + "", + "invalid_answer_xml", + ), + ( + '{"action":"summarize_pack_evidence","args":{}}', + "invalid_answer_xml", + ), + ] + + for response, expected_error in cases: + result = _parse_action(response) + assert result.payload is None + assert result.parse_error == expected_error + + +def test_system_prompt_requires_one_terminal_xml_action(): + system_prompt = prompts.load_system_prompt( + dictionary_terms=["u", "u**3"], + support_size_hint="1 to 3", + ) + + assert system_prompt.count("## Response protocol") == 1 + assert "one ... block wrapping exactly one action" in system_prompt + assert "Never write" in system_prompt + assert "after the closing " in system_prompt + + +def test_candidate_coefficients_match_rendered_equation(): + coefficients = { + "u": -1.25, + "exp(u)-1": 0.5, + "log(1+u**2)": -0.25, + } + equation = candidate.format_equation(coefficients) + + assert candidate.candidate_coefficients(equation, candidate.DEFAULT_DICTIONARY) == coefficients + assert candidate.candidate_coefficients("f(u)=1*u+2*u", ["u"]) == {"u": 3.0} + assert candidate.candidate_coefficients( + "1*u+999*unknown", candidate.DEFAULT_DICTIONARY + ) == {} + + +def test_expanded_dictionary_terms_are_finite_and_round_trip(): + terms = [ + "u**7", + "sin(2*u)", + "u**4/(1+u**4)", + "u/(1+u+u**2)", + ] + u = np.linspace(-2.0, 2.0, 101) + coefficients = {term: float(index + 1) for index, term in enumerate(terms)} + + for term in terms: + assert np.all(np.isfinite(candidate.basis_values(term, u))) + equation = candidate.format_equation(coefficients) + assert candidate.candidate_coefficients(equation, terms) == coefficients + + +def test_physical_hard_ground_truth_family_uses_supported_two_term_templates(): + templates = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_hard", + ) + + assert templates + assert all(len(template["terms"]) == 2 for template in templates) + assert all( + term in candidate.DEFAULT_DICTIONARY + for template in templates + for term in template["terms"] + ) + + +def test_physical_full_ground_truth_family_is_deduplicated_one_to_two_terms(): + def template_key(template): + return ( + tuple(sorted(template["terms"])), + tuple(sorted(template["signs"].items())), + ) + + full = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full", + ) + full_keys = [template_key(template) for template in full] + + assert len(full_keys) == len(set(full_keys)) + assert len(full_keys) == 55 + assert {len(template["terms"]) for template in full} == {1, 2} + assert any(set(template["terms"]) == {"u**3", "u**7"} for template in full) + + +def test_physical_full_train_eval_splits_are_support_disjoint(): + full = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full", + ) + train = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full_train", + ) + evaluation = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full_eval", + ) + + def template_key(template): + return ( + tuple(sorted(template["terms"])), + tuple(sorted(template["signs"].items())), + ) + + train_supports = {frozenset(template["terms"]) for template in train} + eval_supports = {frozenset(template["terms"]) for template in evaluation} + train_terms = {term for template in train for term in template["terms"]} + eval_terms = {term for template in evaluation for term in template["terms"]} + + assert len(train) == 40 + assert len(evaluation) == 15 + assert train_supports.isdisjoint(eval_supports) + assert train_terms == set(candidate.DEFAULT_DICTIONARY) + assert eval_terms <= train_terms + assert {template_key(template) for template in full} == { + template_key(template) for template in [*train, *evaluation] + } + assert {len(template["terms"]) for template in train} == {1, 2} + assert {len(template["terms"]) for template in evaluation} == {2} + + +def test_physical_full_eval_hard_has_unique_two_term_and_varied_three_term_supports(): + train = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full_train", + ) + evaluation = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full_eval_hard", + ) + train_supports = {frozenset(template["terms"]) for template in train} + eval_supports = {frozenset(template["terms"]) for template in evaluation} + train_terms = {term for template in train for term in template["terms"]} + eval_terms = {term for template in evaluation for term in template["terms"]} + + assert len(evaluation) == 18 + assert train_supports.isdisjoint(eval_supports) + assert eval_terms <= train_terms + assert sum(len(template["terms"]) == 2 for template in evaluation) == 12 + assert sum(len(template["terms"]) == 3 for template in evaluation) == 6 + assert len( + { + frozenset(template["terms"]) + for template in evaluation + if len(template["terms"]) == 2 + } + ) == 12 + assert len( + { + frozenset(template["terms"]) + for template in evaluation + if len(template["terms"]) == 3 + } + ) == 6 + + +def test_physical_full_eval_4000_has_held_out_balanced_supports(): + train = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full_train", + ) + evaluation = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full_eval_4000", + ) + train_supports = {frozenset(template["terms"]) for template in train} + eval_supports = {frozenset(template["terms"]) for template in evaluation} + train_terms = {term for template in train for term in template["terms"]} + eval_terms = {term for template in evaluation for term in template["terms"]} + + assert len(evaluation) == 25 + assert len(eval_supports) == 25 + assert train_supports.isdisjoint(eval_supports) + assert eval_terms <= train_terms + assert sum(len(template["terms"]) == 2 for template in evaluation) == 17 + assert sum(len(template["terms"]) == 3 for template in evaluation) == 8 + + +def test_stratified_eval_assignments_balance_every_hidden_template(): + assignments = data_generator.build_stratified_eval_template_assignments( + test_size=288, + eval_pack_size=8, + eval_template_count=18, + ) + + assert assignments is not None + assert len(assignments) == 288 + template_row_counts = {template_index: 0 for template_index in range(18)} + for pack_index in range(36): + pack = assignments[pack_index * 8 : (pack_index + 1) * 8] + assert {row["eval_pack_index"] for row in pack} == {pack_index} + assert len({row["ground_truth_template_index"] for row in pack}) == 1 + assert len({row["ground_truth_template_instance"] for row in pack}) == 1 + template_row_counts[pack[0]["ground_truth_template_index"]] += len(pack) + + assert set(template_row_counts.values()) == {16} + assert { + row["ground_truth_template_instance"] for row in assignments + } == {0, 1} + + +def test_larger_stratified_eval_assignments_use_four_packs_per_template(): + assignments = data_generator.build_stratified_eval_template_assignments( + test_size=576, + eval_pack_size=8, + eval_template_count=18, + ) + + assert assignments is not None + assert len(assignments) == 576 + template_row_counts = Counter( + row["ground_truth_template_index"] for row in assignments + ) + assert set(template_row_counts) == set(range(18)) + assert set(template_row_counts.values()) == {32} + assert { + row["ground_truth_template_instance"] for row in assignments + } == {0, 1, 2, 3} + + +def test_online_eval_subset_keeps_one_fixed_pack_per_template(): + assignments = data_generator.build_stratified_eval_template_assignments( + test_size=576, + eval_pack_size=8, + eval_template_count=18, + ) + + assert assignments is not None + for row in assignments: + row["pde_environment_seed"] = row["eval_pack_index"] + online_assignments = data_generator.select_eval_template_instances( + assignments, + eval_pack_size=8, + eval_template_count=18, + instances_per_template=1, + ) + + assert len(online_assignments) == 144 + assert { + row["ground_truth_template_instance"] for row in online_assignments + } == {0} + template_row_counts = Counter( + row["ground_truth_template_index"] for row in online_assignments + ) + assert set(template_row_counts) == set(range(18)) + assert set(template_row_counts.values()) == {8} + + +def test_stratified_eval_assignments_reject_unbalanced_pack_counts(): + try: + data_generator.build_stratified_eval_template_assignments( + test_size=144, + eval_pack_size=8, + eval_template_count=12, + ) + except ValueError as exc: + assert "must be divisible" in str(exc) + else: + raise AssertionError("Expected unbalanced eval template coverage to fail") + + +def test_fixed_ground_truth_template_index_selects_requested_support(): + templates = ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full_eval_hard", + ) + + for template_index, template in enumerate(templates): + terms, coefficients = ground_truth.sample_hidden_reaction( + np.random.default_rng(1000 + template_index), + templates, + min_terms=1, + max_terms=3, + template_index=template_index, + ) + assert set(terms) == set(template["terms"]) + assert set(coefficients) == set(template["terms"]) + + try: + ground_truth.sample_hidden_reaction( + np.random.default_rng(0), + templates, + min_terms=1, + max_terms=3, + template_index=len(templates), + ) + except ValueError as exc: + assert "outside the eligible template range" in str(exc) + else: + raise AssertionError("Expected an out-of-range template index to fail") + + +def test_initial_condition_amplitudes_follow_trajectory_count(): + amplitude_upper = ground_truth.calibrate_family_initial_amplitude_upper( + ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full_train", + ), + min_terms=1, + max_terms=2, + state_abs_limit=INITIAL_CONDITION_TEST_STATE_ABS_LIMIT, + ) + for trajectory_count in INITIAL_CONDITION_TEST_TRAJECTORY_COUNTS: + amplitudes = pde_numeric.initial_condition_amplitudes( + INITIAL_CONDITION_TEST_PACK_SEED, + trajectory_count, + amplitude_upper=amplitude_upper, + ) + band_edges = np.linspace( + 0.0, + amplitude_upper, + trajectory_count + 1, + ) + + assert len(amplitudes) == trajectory_count + for amplitude, lower, upper in zip( + sorted(amplitudes), band_edges[:-1], band_edges[1:] + ): + assert lower <= amplitude <= upper + assert amplitudes == pde_numeric.initial_condition_amplitudes( + INITIAL_CONDITION_TEST_PACK_SEED, + trajectory_count, + amplitude_upper=amplitude_upper, + ) + + +def test_initial_conditions_obey_sampled_amplitude_and_boundaries(): + x_grid = np.linspace(0.0, 1.0, INITIAL_CONDITION_TEST_GRID_SIZE) + trajectory_count = INITIAL_CONDITION_TEST_TRAJECTORY_COUNTS[-1] + amplitude_upper = ground_truth.calibrate_family_initial_amplitude_upper( + ground_truth.load_hidden_gt_templates( + candidate.DEFAULT_DICTIONARY, + family="physical_full_eval_hard", + ), + min_terms=1, + max_terms=3, + state_abs_limit=INITIAL_CONDITION_TEST_STATE_ABS_LIMIT, + ) + amplitudes = pde_numeric.initial_condition_amplitudes( + INITIAL_CONDITION_TEST_PACK_SEED, + trajectory_count, + amplitude_upper=amplitude_upper, + ) + + for trajectory_index, amplitude in enumerate(amplitudes): + u0 = pde_numeric.initial_condition( + x_grid, + INITIAL_CONDITION_TEST_PACK_SEED, + trajectory_index, + trajectory_count=trajectory_count, + amplitude_upper=amplitude_upper, + shape_config=INITIAL_CONDITION_TEST_SHAPE, + ) + assert u0[0] == 0.0 + assert u0[-1] == 0.0 + assert np.all(u0 >= 0.0) + assert np.isclose(float(np.max(u0)), amplitude) + + +def test_initial_condition_shape_config_parses_mode_range(): + config = pde_numeric.InitialConditionShapeConfig.from_mapping( + { + "mode_count_range": [2, 2], + } + ) + assert config.mode_count_range == (2, 2) + + +def test_reaction_residual_matches_generated_pde_at_terminal_time(): + x_grid = np.linspace(0.0, 1.0, 33) + t_grid = np.linspace(0.0, 0.1, 101) + dx = float(x_grid[1] - x_grid[0]) + dt = float(t_grid[1] - t_grid[0]) + ratio = dt / dx**2 + interior = len(x_grid) - 2 + reaction_fn = lambda u: 2.0 * u + + field = pde_numeric.simulate_dense_trajectory( + x_grid=x_grid, + t_grid=t_grid, + dx=dx, + dt=dt, + lower=-ratio * np.ones(interior - 1), + diag=(1.0 + 2.0 * ratio) * np.ones(interior), + upper=-ratio * np.ones(interior - 1), + trajectory_index=0, + reaction_fn=reaction_fn, + pack_seed=1, + initial_amplitude_upper=1.0, + initial_condition_shape=INITIAL_CONDITION_TEST_SHAPE, + state_abs_limit=INITIAL_CONDITION_TEST_STATE_ABS_LIMIT, + ) + + expected = reaction_fn(field["u"][:, 1:-1]) + np.testing.assert_allclose(field["y"][:, 1:-1], expected, atol=1e-10) + + +def test_regression_error_matches_rendered_candidate(): + u = np.linspace(0.1, 1.0, 100) + y = 1.23456 * u + result = regression.sparse_regression_result( + u=u, + y=y, + u_objective=u, + y_objective=y, + dictionary=["u"], + alpha=0.0, + threshold=0.0, + max_reaction_terms=1, + default_blind_penalty=100.0, + kappa_threshold=100.0, + ) + coefficients = candidate.candidate_coefficients(result["equation"], ["u"]) + actual_error = float( + np.max(np.abs(y - candidate.reaction_value(u, coefficients))) + ) + + assert result["max_error"] == actual_error diff --git a/trinity/algorithm/advantage_fn/__init__.py b/trinity/algorithm/advantage_fn/__init__.py index 6b8aa6d3986..135761aaad7 100644 --- a/trinity/algorithm/advantage_fn/__init__.py +++ b/trinity/algorithm/advantage_fn/__init__.py @@ -22,6 +22,7 @@ "jsd": "trinity.algorithm.advantage_fn.jsd_advantage.JSDAdvantage", "clipb": "trinity.algorithm.advantage_fn.clipb_advantage.ClipBAdvantageFn", "clipv": "trinity.algorithm.advantage_fn.clipv_advantage.ClipVAdvantageFn", + "cod": "trinity.algorithm.advantage_fn.cod_advantage.CoDAdvantageFn", }, ) diff --git a/trinity/algorithm/advantage_fn/cod_advantage.py b/trinity/algorithm/advantage_fn/cod_advantage.py new file mode 100644 index 00000000000..5a3fcd08292 --- /dev/null +++ b/trinity/algorithm/advantage_fn/cod_advantage.py @@ -0,0 +1,461 @@ +"""CoD advantage computation.""" +from typing import Dict, List, Optional, Tuple +import math + +import torch + +from trinity.algorithm.advantage_fn.advantage_fn import AdvantageFn +from trinity.buffer.operators import ExperienceOperator +from trinity.common.experience import Experience, group_by +from trinity.utils.metrics import aggregate_metrics + + +def helper_get_reward(exp: Experience, reward_field: str): + if reward_field == "reward": + return exp.reward + elif reward_field == "total_reward": + return exp.info["total_reward"] + else: + raise ValueError(f"Invalid reward_field {reward_field}") + + +class CoDAdvantageFn(AdvantageFn, ExperienceOperator): + """An advantage function dedicated for CoD.""" + + def __init__( + self, + epsilon: float = 1e-6, + enable_step_norm: bool = False, + std_cal_level: str = "group", # 'group' (task-level) or 'batch' or 'none' + std_threshold: Optional[float] = None, + iterative_hint_e2e_causal: bool = False, + e2e_causal_returns_style: str = "mean", + e2e_causal_returns_window: int = -1, + e2e_causal_returns_gamma: float = 1.0, + e2e_causal_baseline: str = "group-mean", + mask_format_issue_exp: bool = False, + red_weight_temp: Optional[float] = None, + red_weight_adaptive_temp: bool = False, + red_weight_adaptive_version: Optional[str] = None, + red_weight_adaptive_level: Optional[str] = None, + red_weight_adv_shift: bool = False, + **kwargs, + ) -> None: + """Initialize the CoD advantage function. + + Args: + --- original multi-step grpo advantage --- + epsilon (float): A small value to avoid division by zero. + enable_step_norm (bool): If True, normalize advantages by trajectory length. + std_cal_level (str): The scope for calculating reward standard deviation. + 'group' (default): Std is calculated per task group. + 'batch': Std is calculated across all last-step rewards in the entire batch. + 'none': no Std calculation or advantage normalization. + The mean is always calculated per task group. + std_threshold (Optional[float]): If provided, task groups with a reward standard deviation + equal or below this threshold will be skipped. + + --- CoD-specific --- + + iterative_hint_e2e_causal (bool): If True, use iterative hint with end-to-end meta-rl and advantages that respect causality. Options: + - {"style": "mean"} + - {"style": "sliding_window_mean", "window": int} + - {"style": "discounted", "gamma": float} + - e2e_causal_baseline: "group-mean" + - mask_format_issue_exp (bool): if true, mask exp with format issue & positive score + """ + self.epsilon = epsilon + self.enable_step_norm = enable_step_norm + self.std_cal_level = std_cal_level + self.std_threshold = std_threshold + if self.std_cal_level not in ["group", "batch", "none"]: + raise ValueError("std_cal_level must be either 'group' or 'batch' or 'none'") + self.iterative_hint_e2e_causal = iterative_hint_e2e_causal + self.e2e_causal_returns_style = e2e_causal_returns_style + self.e2e_causal_returns_window = e2e_causal_returns_window + self.e2e_causal_returns_gamma = e2e_causal_returns_gamma + self.e2e_causal_baseline = e2e_causal_baseline + self.mask_format_issue_exp = mask_format_issue_exp # DEPRECATED, doesn't take effect + self.red_weight_temp = red_weight_temp + self.red_weight_adaptive_temp = red_weight_adaptive_temp + self.red_weight_adaptive_version = red_weight_adaptive_version + self.red_weight_adaptive_level = red_weight_adaptive_level + self.red_weight_adv_shift = red_weight_adv_shift + + + def calculate_last_step_advantage( + self, + exps: Dict[str, Experience], + precomputed_std: Optional[torch.Tensor] = None, + reward_field: str = "reward", + ) -> Tuple[Dict[str, float], Dict[str, float], bool]: + """Calculate group advantage for a given group of experiences. + + Args: + exps (Dict[str, Experience]): One experience per run, keyed by run ID. + precomputed_std (Optional[torch.Tensor]): Precomputed standard deviation for batch-level calculation. + reward_field: which field to use as reward (for score and advantage calculation). + + Returns: + Dict[str, float]: Scores for each run. + Dict[str, float]: Metrics for logging. + bool: Whether this group should be skipped. + """ + with torch.no_grad(): + if len(exps) == 1: + group_reward_mean = torch.tensor(0.0) + group_reward_std = torch.tensor(1.0) + else: + rewards = torch.tensor([helper_get_reward(exp, reward_field) for exp in exps.values()], dtype=torch.float32) + group_reward_mean = torch.mean(rewards) + group_reward_std = torch.std(rewards) + + # Determine if this group should be skipped based on std_threshold + should_skip = False + if self.std_threshold is not None: + if len(exps) == 1 or group_reward_std <= self.std_threshold: + should_skip = True + + scores = {} + for rid, exp in exps.items(): + exp_reward = helper_get_reward(exp, reward_field) + if self.std_cal_level == "batch" and precomputed_std is not None: + score = (exp_reward - group_reward_mean) / (precomputed_std + self.epsilon) + elif self.std_cal_level == "group": + score = (exp_reward - group_reward_mean) / (group_reward_std + self.epsilon) + elif self.std_cal_level == "none": + score = exp_reward - group_reward_mean + else: + raise ValueError(f"Invalid std_cal_level '{self.std_cal_level}'.") + scores[rid] = score.item() + + # Use standard task rewards for metrics + standard_rewards = torch.tensor([exp.reward for exp in exps.values()], dtype=torch.float32) + standard_reward_mean = torch.mean(standard_rewards) + standard_reward_std = torch.std(standard_rewards) + metrics = { + "reward_mean": standard_reward_mean.item(), + "reward_std": standard_reward_std.item(), + } + + return scores, metrics, should_skip + + def broadcast_advantages( + self, run_exps: Dict[str, List[Experience]], scores: Dict[str, float] + ) -> Dict[str, List[Experience]]: + """Broadcast the calculated advantages to all previous steps in each run. + + Args: + run_exps (Dict[str, List[Experience]]): Experiences grouped by run ID. + scores (Dict[str, float]): Calculated scores for each run. + + Returns: + Dict[str, List[Experience]]: Updated experiences with advantages broadcasted. + """ + for run_id, exps in run_exps.items(): + score = scores[run_id] + traj_length = len(exps) + for exp in exps: + exp.advantages = exp.action_mask * score # type: ignore [operator] + if self.enable_step_norm: + exp.advantages /= traj_length + exp.returns = exp.advantages.clone() + return run_exps + + def process_standard_grpo_custom_score( + self, + exps: List[Experience], + reward_field: str = "reward", + ) -> Tuple[List[Experience], Dict]: + """Standard GRPO with custom field as reward, e.g., per-task reward or total reward in end-to-end meta-rl. + + reward_field: which field to use as reward (for score and advantage calculation) + - "reward": use exp.reward + - "total_reward": use exp.info["total_reward"], tailored to iterative_hint_e2e + """ + if len(exps) == 0: + return [], {} + cnt = 0 + metric_list = [] + filtered_count = 0 + # Step 1: split the experiences into sub-groups by task + task_exps = group_by(exps, "task") + + # --- Pre-computation step for batch-level standard deviation --- + precomputed_std = None + if self.std_cal_level == "batch": + all_laststep_rewards = [] + for task_exp in task_exps.values(): + # First, group all experiences by run to find the last step of each run + task_run_exps = group_by(task_exp, "run") + # Collect rewards from the last step of every run in the entire batch + last_step_rewards = [ + helper_get_reward(run_steps[-1], reward_field) for run_steps in task_run_exps.values() if run_steps + ] + all_laststep_rewards.extend(last_step_rewards) + + if len(all_laststep_rewards) <= 1: + precomputed_std = torch.tensor(1.0) + else: + precomputed_std = torch.std(torch.tensor(all_laststep_rewards, dtype=torch.float32)) + # --- End of pre-computation --- + + # Step 2: further split each task's experiences into sub-groups by run + result_exps = [] + total_task_groups = len(task_exps) + skipped_task_groups = 0 + + for task_exp in task_exps.values(): + run_exps = group_by(task_exp, "run") + + # Step3: extract the last experience (last step) from each run and calculate scores + last_step_exps = {run_id: step_exps[-1] for run_id, step_exps in run_exps.items()} + scores, metrics, should_skip = self.calculate_last_step_advantage( + last_step_exps, + precomputed_std=precomputed_std, + reward_field=reward_field, + ) + + # Skip this task group if std is below threshold + if should_skip: + # Count all experiences in this task group as filtered + task_exp_count = sum(len(step_exps) for step_exps in run_exps.values()) + filtered_count += task_exp_count + skipped_task_groups += 1 + metric_list.append(metrics) + continue + + metric_list.append(metrics) + + # Step 4: broadcast the advantages to all previous steps + run_exps = self.broadcast_advantages(run_exps, scores) + for exps in run_exps.values(): + cnt += len(exps) + result_exps.extend(exps) + + metrics = aggregate_metrics(metric_list, prefix="group_advantages") + metrics["experience_count"] = cnt + metrics["filtered_count"] = filtered_count + + # Calculate the ratio of skipped task groups + if total_task_groups > 0: + metrics["skipped_group_ratio"] = skipped_task_groups / total_task_groups + else: + metrics["skipped_group_ratio"] = 0.0 + + return result_exps, metrics + + def process_standard_grpo(self, exps: List[Experience]) -> Tuple[List[Experience], Dict]: + """Standard GRPO advantage, using exp.reward as reward field.""" + return self.process_standard_grpo_custom_score(exps, "reward") + + def process_iterative_hint_e2e_causal(self, exps: List[Experience]) -> Tuple[List[Experience], Dict]: + """Refined advantage for cross-task e2e case, respecting causal relationship. + + Assume each exp has exp.info fields: + - "exp_type": "solve_task" or "gen_hint" + - "task_idx" (0, 1, 2, ...) or "hint_idx" (0, 1, ...) + - "task_rewards": List[float], list of task/episode-wise outcome rewards within the same meta-trajectory + """ + metrics = dict() + metrics["experience_count"] = len(exps) + + # Drop trajectories the judge flagged (own- or trajectory-level). + def _is_bad(e: Experience) -> bool: + return ( + e.info.get("judge_format_error", False) + or e.info.get("judge_format_error_in_trajectory", False) + ) + + bad_count = sum(1 for e in exps if _is_bad(e)) + exps = [e for e in exps if not _is_bad(e)] + metrics["judge_format_error_count"] = bad_count + + # Step 1. For each exp, calculate exp.info["returns_norm"], plus concise record for Step 2 + record_returns = dict() + + for exp in exps: + task_rewards = exp.info["task_rewards"] # List[float] + exp_type = exp.info["exp_type"] + + # Get rewards-to-go (for gen_hint exp, also include current exp's own reward) + if exp_type == "solve_task": + idx = int(exp.info["task_idx"]) + rewards_to_go = task_rewards[idx:] + elif exp_type == "gen_hint": + idx = int(exp.info["hint_idx"]) + rewards_to_go = [exp.reward] + task_rewards[(idx + 1) : ] + else: + raise ValueError(f"Invalid exp_type {exp_type}, something went wrong.") + rewards_to_go = [float(r) for r in rewards_to_go] # mitigate dtype error in torch.tensor/sum below + + # Calculate returns + e2e_causal_returns_style = self.e2e_causal_returns_style + if e2e_causal_returns_style == "mean": + exp_returns_norm = torch.mean(torch.tensor(rewards_to_go)).item() + elif e2e_causal_returns_style == "sliding_window_mean": + window = int(self.e2e_causal_returns_window) + assert window >= 1, f"Expect sliding-window size >= 1, get {window}." + rewards_to_go_window = rewards_to_go[ : min(window, len(rewards_to_go))] + exp_returns_norm = torch.mean(torch.tensor(rewards_to_go_window)).item() + elif e2e_causal_returns_style == "discounted": + gamma = float(self.e2e_causal_returns_gamma) + assert 0.0 <= gamma <= 1.0, f"Expect gamma within range [0, 1], get {gamma}." + rewards_weights = [gamma ** i for i in range(len(rewards_to_go))] + exp_returns_norm = torch.sum(torch.tensor(rewards_to_go) * torch.tensor(rewards_weights)).item() + else: + raise ValueError(f"Invalid e2e_causal_returns_style: {e2e_causal_returns_style}.") + + exp.info["returns_norm"] = exp_returns_norm + + key_for_record = "_".join([str(exp.eid.task), exp_type, str(idx)]) + exp.info["key_for_record"] = key_for_record + if key_for_record not in record_returns: + record_returns[key_for_record] = dict() + if exp.eid.run not in record_returns[key_for_record]: + record_returns[key_for_record][exp.eid.run] = exp_returns_norm + + # Step 2. Calculate episode-wise baseline within each meta-task, then calculate advantage for each exp + episode_wise_baselines = dict() + for key, value in record_returns.items(): + if self.e2e_causal_baseline == "group-mean": + baseline = torch.mean(torch.tensor(list(value.values()))).item() + else: + raise ValueError(f"Invalid e2e_causal_baseline: {self.e2e_causal_baseline}") + episode_wise_baselines[key] = baseline + + # Compute per-exp score (= returns_norm - baseline) + for exp in exps: + key = exp.info["key_for_record"] + baseline = episode_wise_baselines[key] + exp.info["e2e_causal_score"] = exp.info["returns_norm"] - baseline + exp.info["e2e_causal_resp_len"] = torch.sum(exp.action_mask).item() + + # Helper functions for re-weighting + def estimate_adv_sum(lst_scores, lst_resp_len, temp): + if temp is None: + return sum([lst_scores[i] * lst_resp_len[i] for i in range(len(lst_scores))]) + return sum( + [lst_scores[i] * math.exp(lst_scores[i] / temp) * lst_resp_len[i] for i in range(len(lst_scores))] + ) + + def helper_process_lst_exps(lst_exps): + # Decide the re-weight temperature: adaptive (set so that token-wise mean + # advantage is non-negative) or the fixed red_weight_temp. + if self.red_weight_adaptive_temp: + weight_temp = None + assert self.red_weight_adaptive_version == "bisection", f"Invalid red_weight_adaptive_version: {self.red_weight_adaptive_version}" + lst_scores = [exp.info["e2e_causal_score"] for exp in lst_exps] + lst_resp_len = [exp.info["e2e_causal_resp_len"] for exp in lst_exps] + if estimate_adv_sum(lst_scores, lst_resp_len, None) < 0: + temp_low = 0.8 # avoid too aggressive weighting + temp_high = 100.0 + for _ in range(20): + temp_mid = (temp_low + temp_high) / 2 + if estimate_adv_sum(lst_scores, lst_resp_len, temp_mid) < 0: + temp_high = temp_mid + else: + temp_low = temp_mid + if temp_high - temp_low < 0.05: + break + weight_temp = temp_mid + + else: + weight_temp = self.red_weight_temp + if weight_temp: + assert weight_temp > 0.0, "red_weight_temp must be positive float." + + # Calculate advantage shift to achieve zero token-mean + adv_mean = None + if self.red_weight_adv_shift: + tensor_scores = torch.tensor([exp.info["e2e_causal_score"] for exp in lst_exps], dtype=torch.float32) + tensor_resp_len = torch.tensor([exp.info["e2e_causal_resp_len"] for exp in lst_exps], dtype=torch.float32) + adv_mean = torch.sum(tensor_scores * tensor_resp_len).item() / torch.sum(tensor_resp_len).item() + + # Adaptively re-weight or shift scores, then set advantage and returns + for exp in lst_exps: + score = exp.info["e2e_causal_score"] + if weight_temp: + score *= math.exp(score / weight_temp) + if adv_mean: + if adv_mean < 0: + score -= adv_mean + exp.advantages = exp.action_mask * score + exp.returns = exp.action_mask * exp.info["returns_norm"] + + return lst_exps, weight_temp + + # Group exps by specified level, then process exps for each group + if self.red_weight_adaptive_level == "task": + grouped_exps = group_by(exps, "task") + elif self.red_weight_adaptive_level == "taskset": + def group_by_taskset_id(experiences: List[Experience]) -> Dict[str, List[Experience]]: + """Group experiences by taskset_id. (Modified from `group_by`)""" + grouped = {} + for exp in experiences: + group_id = str(exp.info["taskset_id"]) # taskset_id: int + if group_id not in grouped: + grouped[group_id] = [] + grouped[group_id].append(exp) + return grouped + grouped_exps = group_by_taskset_id(exps) + else: + grouped_exps = {"0": exps} + + num_groups = len(grouped_exps) + print(f"!!! CoD-advantage debug: number of groups = {num_groups} !!!") + + result_exps = [] + weight_temps = [] + for lst_exps in grouped_exps.values(): + processed_lst_exps, weight_temp = helper_process_lst_exps(lst_exps) + result_exps.extend(processed_lst_exps) + weight_temps.append(weight_temp) + + # Diagnostic metric for re-weighting mechanism + active_weight_temps = [temp for temp in weight_temps if temp is not None] + if len(active_weight_temps) > 0: + metrics["active_weight_temp_mean"] = sum(active_weight_temps) / len(active_weight_temps) + else: + metrics["active_weight_temp_mean"] = 0.0 + + return result_exps, metrics + + def process(self, exps: List[Experience]) -> Tuple[List[Experience], Dict]: + if self.iterative_hint_e2e_causal: + return self.process_iterative_hint_e2e_causal(exps) + else: + return self.process_standard_grpo(exps) + + def __call__(self, exps, **kwargs): + """Not used, `process` is the method being called.""" + if self.iterative_hint_e2e_causal: + return self.process_iterative_hint_e2e_causal(exps) + else: + return self.process_standard_grpo(exps) + + @classmethod + def compute_in_trainer(cls) -> bool: + """Whether the advantage should be computed in the trainer loop.""" + return False + + @classmethod + def default_args(cls) -> Dict: + """Return the default configuration for this strategy.""" + return { + "epsilon": 1e-6, + "enable_step_norm": False, + "std_threshold": None, + "std_cal_level": "group", + "iterative_hint_e2e_causal": False, + "e2e_causal_returns_style": "mean", + "e2e_causal_returns_window": -1, + "e2e_causal_returns_gamma": 1.0, + "e2e_causal_baseline": "group-mean", + "mask_format_issue_exp": False, + "red_weight_temp": None, + "red_weight_adaptive_temp": False, + "red_weight_adaptive_version": None, + "red_weight_adaptive_level": None, + "red_weight_adv_shift": False, + } diff --git a/trinity/algorithm/advantage_fn/multi_step_grpo_advantage.py b/trinity/algorithm/advantage_fn/multi_step_grpo_advantage.py index 80b4d568c52..5f6b5f044e7 100644 --- a/trinity/algorithm/advantage_fn/multi_step_grpo_advantage.py +++ b/trinity/algorithm/advantage_fn/multi_step_grpo_advantage.py @@ -20,7 +20,7 @@ def __init__( self, epsilon: float = 1e-6, enable_step_norm: bool = False, - std_cal_level: str = "group", # 'group' (task-level) or 'batch' + std_cal_level: str = "group", # 'group' (task-level) or 'batch' or 'none' std_threshold: Optional[float] = None, **kwargs, ) -> None: @@ -32,6 +32,7 @@ def __init__( std_cal_level (str): The scope for calculating reward standard deviation. 'group' (default): Std is calculated per task group. 'batch': Std is calculated across all last-step rewards in the entire batch. + 'none': no Std calculation or advantage normalization. The mean is always calculated per task group. std_threshold (Optional[float]): If provided, task groups with a reward standard deviation equal or below this threshold will be skipped. @@ -40,8 +41,8 @@ def __init__( self.enable_step_norm = enable_step_norm self.std_cal_level = std_cal_level self.std_threshold = std_threshold - if self.std_cal_level not in ["group", "batch"]: - raise ValueError("std_cal_level must be either 'group' or 'batch'") + if self.std_cal_level not in ["group", "batch", "none"]: + raise ValueError("std_cal_level must be either 'group' or 'batch' or 'none'") def calculate_last_step_advantage( self, @@ -78,8 +79,12 @@ def calculate_last_step_advantage( for rid, exp in exps.items(): if self.std_cal_level == "batch" and precomputed_std is not None: score = (exp.reward - group_reward_mean) / (precomputed_std + self.epsilon) - else: + elif self.std_cal_level == "group": score = (exp.reward - group_reward_mean) / (group_reward_std + self.epsilon) + elif self.std_cal_level == "none": + score = exp.reward - group_reward_mean + else: + raise ValueError(f"Invalid std_cal_level '{self.std_cal_level}'.") scores[rid] = score.item() metrics = { "reward_mean": group_reward_mean.item(), diff --git a/trinity/algorithm/algorithm.py b/trinity/algorithm/algorithm.py index 2cdf4cc08e1..252dd695679 100644 --- a/trinity/algorithm/algorithm.py +++ b/trinity/algorithm/algorithm.py @@ -584,7 +584,7 @@ class OnPolicyDistillAlgorithm(AlgorithmType): """ use_critic: bool = False - use_reference: bool = False + use_reference: bool = True # !!! PATCH FOR COD !!! compute_advantage_in_trainer: bool = True # advantage_fn computes from teacher_logprobs can_balance_batch: bool = True schema: str = "experience" diff --git a/trinity/algorithm/policy_loss_fn/rec_policy_loss.py b/trinity/algorithm/policy_loss_fn/rec_policy_loss.py index 6f00ac86c17..0905c74c443 100644 --- a/trinity/algorithm/policy_loss_fn/rec_policy_loss.py +++ b/trinity/algorithm/policy_loss_fn/rec_policy_loss.py @@ -1,7 +1,7 @@ """REC-token policy loss function. """ -from typing import Dict, Tuple +from typing import Dict, Optional, Tuple import torch @@ -22,6 +22,8 @@ def __init__( regularizer: str = "none", regularizer_coef: float = 0.0, temp: float = 1.0, + loss_agg_mode: Optional[str] = "token-mean", + fix_opd_advantage: bool = False, ) -> None: super().__init__(backend=backend) @@ -49,6 +51,7 @@ def __init__( assert self.weight in [ "none", "importance_sampling", + "truncated_importance_sampling", "gspo_importance_sampling", "advantage", ], f"Invalid weight: {self.weight}" @@ -64,6 +67,9 @@ def __init__( self.temp = temp assert self.temp > 0.0, f"Invalid temp: {self.temp}" + self.loss_agg_mode = loss_agg_mode + self.fix_opd_advantage = fix_opd_advantage + def __call__( # type: ignore self, logprob: torch.Tensor, # [batch_size, seq_len] @@ -105,12 +111,21 @@ def __call__( # type: ignore if self.weight == "importance_sampling": advantages = advantages * ratio # importance sampling + elif self.weight == "truncated_importance_sampling": + advantages = advantages * torch.clamp(ratio, 1 - self.epsilon_low_prime, 1 + self.epsilon_high_prime) elif self.weight == "gspo_importance_sampling": advantages = advantages * normalized_seq_ratio elif self.weight == "advantage": weight = torch.exp(advantages / self.temp) advantages = advantages * weight # advantage weighting (unnormalized version) + if self.fix_opd_advantage: + # Fix advantage calculation for running OPD with off-policyness. Rationale: + # - Implementation of OPD advantage in Trinity gives teacher_logprob - old_logprob (assuming kl_coef = 1.0) + # - Targeted OPD advantage = teacher_logprob - logprob.detach() (by current policy) + # - Hence the fix is to add to advantage: old_logprob - logprob.detach() + advantages = advantages + old_logprob - logprob.detach() + pg_losses = -advantages * logprob * is_in_range.float() if self.regularizer == "forward-kl": @@ -121,15 +136,14 @@ def __call__( # type: ignore regularizer_losses = self.regularizer_coef * (logprob - old_logprob).square() pg_losses = pg_losses + regularizer_losses + loss_agg_mode = self.loss_agg_mode if self.clip_mode == "gspo-one-side": - # [EXPERIMENTAL] specialized for gspo-style rec variant for now - pg_loss = aggregate_loss( - values=pg_losses, - mask=action_mask, - loss_agg_mode="seq-mean-token-mean", - ) - else: - pg_loss = masked_mean(pg_losses, action_mask) + loss_agg_mode = "seq-mean-token-mean" + pg_loss = aggregate_loss( + values=pg_losses, + mask=action_mask, + loss_agg_mode=loss_agg_mode, + ) pg_clipfrac = masked_mean(is_clipped_mask.float(), action_mask) metrics = { @@ -150,4 +164,6 @@ def default_args(cls) -> Dict: "regularizer": "none", "regularizer_coef": 0.0, "temp": 1.0, + "loss_agg_mode": "token-mean", + "fix_opd_advantage": False, } diff --git a/trinity/buffer/operators/__init__.py b/trinity/buffer/operators/__init__.py index 35baa7dad21..bdb443745ce 100644 --- a/trinity/buffer/operators/__init__.py +++ b/trinity/buffer/operators/__init__.py @@ -16,6 +16,8 @@ "pass_rate_calculator": "trinity.buffer.operators.mappers.pass_rate_calculator.PassRateCalculator", "data_juicer": "trinity.buffer.operators.data_juicer_operator.DataJuicerOperator", "invalid_reward_filter": "trinity.buffer.operators.filters.reward_filter.InvalidRewardFilter", + "cod_advantage_fn": "trinity.algorithm.advantage_fn.cod_advantage.CoDAdvantageFn", + "cod_overwrite_reward_with_returns": "trinity.buffer.operators.cod_operator.OverwriteRewardWithReturns", }, ) diff --git a/trinity/buffer/operators/cod_operator.py b/trinity/buffer/operators/cod_operator.py new file mode 100644 index 00000000000..575e8d2f997 --- /dev/null +++ b/trinity/buffer/operators/cod_operator.py @@ -0,0 +1,14 @@ +from typing import List, Tuple + +from trinity.buffer.operators import ExperienceOperator +from trinity.common.experience import Experience + + +class OverwriteRewardWithReturns(ExperienceOperator): + """Overwrite exp.reward with exp.info["returns_norm"], dedicated to CoD-PPO algorithm.""" + + def process(self, exps: List[Experience]) -> Tuple[List[Experience], dict]: + result_exps = [exp for exp in exps] + for exp in result_exps: + exp.reward = exp.info["returns_norm"] + return result_exps, {} diff --git a/trinity/common/config.py b/trinity/common/config.py index 6d27cb3af87..ac2c5eee742 100644 --- a/trinity/common/config.py +++ b/trinity/common/config.py @@ -808,7 +808,7 @@ class TrainerConfig: trainer_type: str = "verl" trainer_strategy: str = "fsdp2" # "fsdp", "fsdp2" or "megatron" save_interval: int = 0 - enable_preview: bool = False # enable rollout preview in wandb + enable_preview: bool = True # enable rollout preview in wandb total_steps: Optional[ int ] = None # total training steps, training stops when reaching this step, None means no limit @@ -936,6 +936,19 @@ class StageConfig: trainer: Optional[TrainerConfig] = None +@dataclass +class CoDConfig: + """!!! Config for CoD !!!""" + + # number of original tasks to be packed into one task + task_pack_size: int = 4 + eval_task_pack_size: Optional[int] = None + packing_strategy: str = "cod" + + # cod workflow args: log_dir, exp_name, etc. + cod_workflow_args: dict = field(default_factory=dict) + + @dataclass class Config: """Global Configuration""" @@ -967,6 +980,9 @@ class Config: service: ServiceConfig = field(default_factory=ServiceConfig) log: LogConfig = field(default_factory=LogConfig) + # !!! cod config !!! + cod: CoDConfig = field(default_factory=CoDConfig) + # configurations for different training stages stages: List[StageConfig] = field(default_factory=list) diff --git a/trinity/common/experience.py b/trinity/common/experience.py index ef6c91429f7..aed20d2a6d8 100644 --- a/trinity/common/experience.py +++ b/trinity/common/experience.py @@ -222,9 +222,13 @@ def __init__( # noqa: C901 ), f"Token ids must be larger than the prompt length. Got len(tokens)={len(tokens)}, prompt_length={prompt_length}." action_mask = torch.ones(len(tokens) - prompt_length, dtype=torch.bool) else: + # mask out exp if prompt truncated action_mask = torch.zeros(len(logprobs), dtype=torch.bool) elif experience_type == "dpo": prompt_length = len(tokens) + # Note: For multi_turn, action_mask is passed in from outside. + # Truncation handling for multi_turn (concatenated) is done in convert_messages_to_experience, + # which only masks the incomplete turn, preserving complete turns. if eid is None: self.eid = EID() elif isinstance(eid, dict): diff --git a/trinity/common/models/model.py b/trinity/common/models/model.py index bc3050906cb..1a00cca3fe0 100644 --- a/trinity/common/models/model.py +++ b/trinity/common/models/model.py @@ -353,6 +353,9 @@ async def convert_messages_to_experience( prompt_length=prompt_length, action_mask=action_mask[prompt_length:], # Exclude the prompt tokens messages=messages, + # !!! MODIFICATION FOR COD START !!! + response_text=self.tokenizer.decode(token_ids[prompt_length:]) if prompt_length < len(token_ids) else None, + # !!! MODIFICATION FOR COD END !!! truncate_status=truncate_status, ) diff --git a/trinity/common/models/vllm_model.py b/trinity/common/models/vllm_model.py index b30e83e8287..6d055ab6bd8 100644 --- a/trinity/common/models/vllm_model.py +++ b/trinity/common/models/vllm_model.py @@ -8,7 +8,7 @@ import numpy as np import torch from packaging.version import parse as parse_version -from transformers import AutoProcessor +from transformers import AutoProcessor, AutoTokenizer from trinity.common.config import InferenceModelConfig from trinity.common.constants import SyncMethod @@ -95,10 +95,19 @@ def __init__( async def _initialize_tokenizer(self): if self.tokenizer is None: + # !!! PATCH FOR COD START !!! if self.vllm_version >= parse_version("0.15.0"): - self.tokenizer = self.async_llm.get_tokenizer() + engine_tokenizer = self.async_llm.get_tokenizer() else: - self.tokenizer = await self.async_llm.get_tokenizer() + engine_tokenizer = await self.async_llm.get_tokenizer() + # Standalone tokenizer to avoid the "Already borrowed" race, keeping + # the engine's model_max_length so truncation stays aligned. + self.tokenizer = AutoTokenizer.from_pretrained( + self.config.model_path, + trust_remote_code=self.config.trust_remote_code, + ) + self.tokenizer.model_max_length = engine_tokenizer.model_max_length + # !!! PATCH FOR COD END !!! self.tokenizer.truncation_side = "left" async def _initialize_processor(self): diff --git a/trinity/common/workflows/__init__.py b/trinity/common/workflows/__init__.py index 7627cdca296..cbda8e195c8 100644 --- a/trinity/common/workflows/__init__.py +++ b/trinity/common/workflows/__init__.py @@ -52,6 +52,16 @@ "on_policy_distill_math_workflow": "trinity.common.workflows.on_policy_distill_workflow.OnPolicyDistillMathWorkflow", # custom workflows "sudoku_workflow": "trinity.common.workflows.envs.sudoku.sudoku_workflow.SudokuWorkflow", + # connect-the-dots (cod) + "cod_workflow": "trinity.common.workflows.connect_the_dots.cod_workflow.CoDWorkflow", + # cod workflows (unified prompt management) + "cod_frozenlake_obscure_workflow": "trinity.common.workflows.connect_the_dots.frozen_lake.workflow_obscure.CoDFrozenLakeObscureWorkflow", + "cod_frozenlake_obscure_react_workflow": "trinity.common.workflows.connect_the_dots.frozen_lake.workflow_obscure_react.CoDFrozenLakeObscureReActWorkflow", + "cod_grid_navigation_workflow": "trinity.common.workflows.connect_the_dots.grid_navigation.workflow.CoDGridNavigationWorkflow", + "cod_random_alchemy_workflow": "trinity.common.workflows.connect_the_dots.alchemy.workflow_random.CoDRandomAlchemyWorkflow", + "cod_terminal_workflow": "trinity.common.workflows.connect_the_dots.terminal.workflow.CoDTerminalWorkflow", + "cod_learn2ask_workflow": "trinity.common.workflows.connect_the_dots.learn2ask.workflow.CoDLearn2AskWorkflow", + "cod_optimalcontrol_workflow": "trinity.common.workflows.connect_the_dots.optimalcontrol.workflow.CoDOptimalControlWorkflow", # !!! }, ) diff --git a/trinity/common/workflows/connect_the_dots/agentscope_utils.py b/trinity/common/workflows/connect_the_dots/agentscope_utils.py new file mode 100644 index 00000000000..257441a68c1 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/agentscope_utils.py @@ -0,0 +1,224 @@ +"""AgentScope adapters used by CoD workflows.""" + +import json +from typing import Any, List, Optional + +try: + from agentscope.agent import AgentBase, ReActAgent + from agentscope.formatter import OpenAIChatFormatter + from agentscope.memory import InMemoryMemory + from agentscope.message import Msg + from agentscope.model import OpenAIChatModel + from agentscope.tool import Toolkit +except Exception as e: + _AGENTSCOPE_IMPORT_ERROR = e +else: + _AGENTSCOPE_IMPORT_ERROR = None + + +def _ensure_agentscope() -> None: + if _AGENTSCOPE_IMPORT_ERROR is not None: + raise ImportError( + "AgentScope is not installed or failed to import. Please install it " + "before running CoD AgentScope workflows." + ) from _AGENTSCOPE_IMPORT_ERROR + + +def _build_model_generate_kwargs(model) -> dict: + """Build AgentScope generate kwargs from rollout model config.""" + config = model.config + kwargs = { + "temperature": config.temperature, + "top_p": config.top_p, + "max_tokens": config.max_response_tokens, + } + extra_body = {} + if config.top_k is not None: + extra_body["top_k"] = config.top_k + if config.min_response_tokens is not None: + extra_body["min_tokens"] = config.min_response_tokens + if config.repetition_penalty is not None: + extra_body["repetition_penalty"] = config.repetition_penalty + if extra_body: + kwargs["extra_body"] = extra_body + return kwargs + + +def _build_rollout_generate_kwargs(rollout_args) -> dict: + """Build AgentScope generate kwargs from task rollout_args.""" + kwargs = { + "temperature": rollout_args.temperature, + "top_p": rollout_args.top_p, + } + if rollout_args.max_tokens is not None: + kwargs["max_tokens"] = rollout_args.max_tokens + if rollout_args.top_k != -1: + kwargs["extra_body"] = {"top_k": rollout_args.top_k} + return kwargs + + +def _build_agentscope_chat_model(model_path: str, client, generate_kwargs: dict): + """Build an AgentScope OpenAIChatModel with Trinity's recording client.""" + _ensure_agentscope() + + chat_model = OpenAIChatModel( + api_key="EMPTY", + model_name=model_path, + stream=False, + generate_kwargs=generate_kwargs, + ) + chat_model.client = client + return chat_model + + +async def _build_agentscope_chat_model_from_trinity(model, generate_kwargs: dict): + """Build an AgentScope chat model from a Trinity ModelWrapper.""" + model_path = model.model_path + client = model.get_openai_async_client() + return _build_agentscope_chat_model( + model_path=model_path, + client=client, + generate_kwargs=generate_kwargs, + ) + + +async def build_agentscope_single_turn_agent(*, name: str, model, rollout_args): + """Build an AgentScope agent that maps messages to one model call.""" + _ensure_agentscope() + + class _SingleTurnAgent(AgentBase): + """Single-turn agent: messages -> one model call -> assistant Msg.""" + + def __init__(self, model, formatter): + super().__init__() + self.name = name + self.model = model + self.formatter = formatter + + async def reply(self, messages: List[dict]) -> "Msg": + msgs = [Msg(m["role"], m["content"], m["role"]) for m in messages] + res = await self.model(await self.formatter.format(msgs=msgs)) + return Msg(self.name, res.content, "assistant") + + async def observe(self, msg=None) -> None: + pass + + async def handle_interrupt(self, *args, **kwargs) -> "Msg": + return Msg(self.name, "Interrupted.", "assistant") + + generate_kwargs = _build_rollout_generate_kwargs(rollout_args) + chat_model = await _build_agentscope_chat_model_from_trinity(model, generate_kwargs) + return _SingleTurnAgent(chat_model, OpenAIChatFormatter()) + + +async def build_agentscope_react_agent( + *, + name: str, + model, + system_prompt: str, + compress_assistant_fn, + toolkit=None, + max_iters: int = 1, +): + """Build an AgentScope ReActAgent with compressed assistant text memory.""" + _ensure_agentscope() + + class _CompressingMemory(InMemoryMemory): + """Compress text-only assistant messages without touching tool blocks.""" + + async def add(self, memories, marks=None, allow_duplicates=False, **kwargs): + if memories is not None: + msgs = memories if isinstance(memories, list) else [memories] + out: List[Optional[Msg]] = [] + for msg in msgs: + has_tool_blocks = ( + msg is not None + and ( + msg.has_content_blocks("tool_use") + or msg.has_content_blocks("tool_result") + ) + ) + if ( + msg is not None + and msg.role == "assistant" + and not has_tool_blocks + ): + text = msg.get_text_content() or "" + msg = Msg( + msg.name, + compress_assistant_fn(text), + "assistant", + ) + out.append(msg) + memories = out if isinstance(memories, list) else out[0] + await super().add( + memories, marks=marks, allow_duplicates=allow_duplicates, **kwargs + ) + + generate_kwargs = _build_model_generate_kwargs(model) + chat_model = await _build_agentscope_chat_model_from_trinity(model, generate_kwargs) + return ReActAgent( + name=name, + sys_prompt=system_prompt, + model=chat_model, + formatter=OpenAIChatFormatter(), + toolkit=toolkit if toolkit is not None else Toolkit(), + memory=_CompressingMemory(), + max_iters=max_iters, + ) + + +async def run_agentscope_agent_step(agent, user_content: str) -> str: + """Run one AgentScope reply and return final text.""" + _ensure_agentscope() + + reply = await agent.reply(Msg("user", user_content, role="user")) + return reply.get_text_content() or "" + + +def _format_tool_output(output: Any) -> str: + if isinstance(output, str): + return output + if isinstance(output, list): + parts = [] + for item in output: + if isinstance(item, dict) and item.get("type") == "text": + parts.append(str(item.get("text", ""))) + else: + parts.append(json.dumps(item, ensure_ascii=False)) + return "\n".join(parts) + return json.dumps(output, ensure_ascii=False) + + +def agentscope_msg_to_text(msg) -> str: + """Render one AgentScope message for CoD logs and trajectories.""" + parts = [] + for block in msg.get_content_blocks(): + block_type = block.get("type") + if block_type == "text": + parts.append(block.get("text", "")) + elif block_type == "tool_use": + tool_input = json.dumps(block.get("input", {}), ensure_ascii=False) + parts.append(f"[tool_call] {block.get('name')}({tool_input})") + elif block_type == "tool_result": + output = _format_tool_output(block.get("output", "")) + parts.append(f"[tool_result] {block.get('name')}: {output}") + elif block_type == "thinking": + parts.append(f"[thinking] {block.get('thinking', '')}") + else: + parts.append(f"[{block_type}]") + content = "\n".join(part for part in parts if part) + if not content: + return "" + label = "Tool" if msg.has_content_blocks("tool_result") else msg.role.capitalize() + return f"{label}: {content}" + + +def agentscope_msgs_to_text(messages: List[Any]) -> str: + """Render AgentScope messages as a compact transcript.""" + lines = [] + for msg in messages: + rendered = agentscope_msg_to_text(msg) + if rendered.strip(): + lines.append(rendered) + return "\n".join(lines) diff --git a/trinity/common/workflows/connect_the_dots/alchemy/__init__.py b/trinity/common/workflows/connect_the_dots/alchemy/__init__.py new file mode 100644 index 00000000000..57ef39a4422 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/alchemy/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +from trinity.common.workflows.connect_the_dots.alchemy.env import AlchemyEnv +from trinity.common.workflows.connect_the_dots.alchemy.common import GameInstance + +__all__ = ["AlchemyEnv", "GameInstance"] diff --git a/trinity/common/workflows/connect_the_dots/alchemy/common.py b/trinity/common/workflows/connect_the_dots/alchemy/common.py new file mode 100644 index 00000000000..584d3a580f9 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/alchemy/common.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +"""Shared types and data structures for Alchemy game variants.""" + +from dataclasses import dataclass +from typing import Dict, List, Optional, Set, Tuple + +# Recipe key: tuple(sorted([e1, e2])) to handle both order and self-combination +RecipeKey = Tuple[str, str] + + +@dataclass +class GameInstance: + """A complete definition of one Alchemy game.""" + + target: str + recipes: Dict[RecipeKey, str] # {("air","air"): "pressure", ...} + starting_inventory: Dict[str, int] # {element: count} available at start + all_elements: Set[str] # All elements in this subgraph + solution_path: List[Tuple[str, str, str]] # [(e1, e2, result), ...] + depth: int # Depth of target element + local_tiers: Dict[str, int] = None # Tiers computed within this subgraph + + def __post_init__(self): + if self.local_tiers is None: + self.local_tiers = self._compute_local_tiers() + # Filter out recipes that violate local tier ordering + self.recipes = { + key: result for key, result in self.recipes.items() + if not (key[0] in self.local_tiers and key[1] in self.local_tiers and result in self.local_tiers) + or self.local_tiers[result] > max(self.local_tiers[key[0]], self.local_tiers[key[1]]) + } + + def _compute_local_tiers(self) -> Dict[str, int]: + """Compute tiers from recipe graph structure. + + Leaf elements (not produced by any recipe) are tier 0. + Other elements: tier = max(tier(input1), tier(input2)) + 1. + """ + all_ingredients: Set[str] = set() + all_results: Set[str] = set() + for key, result in self.recipes.items(): + all_ingredients.update(key) + all_results.add(result) + leaves = (all_ingredients | set(self.starting_inventory)) - all_results + + tiers = {elem: 0 for elem in leaves} + changed = True + while changed: + changed = False + for key, result in self.recipes.items(): + if result in tiers: + continue + e1, e2 = key + if e1 in tiers and e2 in tiers: + tiers[result] = max(tiers[e1], tiers[e2]) + 1 + changed = True + return tiers diff --git a/trinity/common/workflows/connect_the_dots/alchemy/env.py b/trinity/common/workflows/connect_the_dots/alchemy/env.py new file mode 100644 index 00000000000..547e13662ec --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/alchemy/env.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +"""Gymnasium environment for the Alchemy crafting game.""" + +from typing import Any, Dict, List, Optional, Tuple + +import gymnasium as gym + +from trinity.common.workflows.connect_the_dots.alchemy.common import GameInstance, RecipeKey + + +class AlchemyEnv(gym.Env): + """Alchemy crafting game environment. + + Supports two material modes: + - "limited": elements are consumed on every attempt (success or failure). + - "unlimited": elements are never consumed; new discoveries are added to inventory. + + Game ends when target is synthesized, round limit is reached, or (limited only) + materials run out. + """ + + metadata = {"render_modes": ["text"]} + + def __init__(self, game_instance: GameInstance, max_rounds: int = 35, + material_mode: str = "limited", render_mode: str = "text"): + super().__init__() + self.game = game_instance + self.max_rounds = max_rounds + self.material_mode = material_mode + self.render_mode = render_mode + self.inventory: Dict[str, int] = {} + self.discovered_recipes: List[Tuple[str, str, str]] = [] + self.failed_combinations: List[Tuple[str, str]] = [] + self.current_round: int = 0 + self._last_result: Optional[str] = None + self._invalid_action: bool = False + self._success: bool = False + + def reset(self, seed=None, options=None) -> Tuple[str, Dict[str, Any]]: + super().reset(seed=seed) + if self.material_mode == "unlimited": + self.inventory = {k: 1 for k in self.game.starting_inventory} + else: + self.inventory = dict(self.game.starting_inventory) + self.discovered_recipes = [] + self.failed_combinations = [] + self.current_round = 0 + self._last_result = None + self._invalid_action = False + self._success = False + return self.render(), self._get_info() + + def step(self, action: Tuple[str, str]) -> Tuple[str, float, bool, bool, Dict[str, Any]]: + raw1, raw2 = action + self.current_round += 1 + self._last_result = None + self._invalid_action = False + + # Normalize to inventory keys (case-insensitive) + inv_lower = {k.lower(): k for k in self.inventory} + elem1 = inv_lower.get(raw1.lower(), raw1) + elem2 = inv_lower.get(raw2.lower(), raw2) + + # Validate elements exist in inventory + if self.material_mode == "unlimited": + # Unlimited: just check both elements exist (no quantity requirement) + if self.inventory.get(elem1, 0) < 1 or self.inventory.get(elem2, 0) < 1: + self._invalid_action = True + else: + if elem1 == elem2: + if self.inventory.get(elem1, 0) < 2: + self._invalid_action = True + else: + if self.inventory.get(elem1, 0) < 1 or self.inventory.get(elem2, 0) < 1: + self._invalid_action = True + + if self._invalid_action: + return self._finish_step() + + # Consume ingredients (limited mode only) + if self.material_mode == "limited": + if elem1 == elem2: + self.inventory[elem1] -= 2 + else: + self.inventory[elem1] -= 1 + self.inventory[elem2] -= 1 + self.inventory = {k: v for k, v in self.inventory.items() if v > 0} + + # Lookup recipe + key: RecipeKey = tuple(sorted([elem1.lower(), elem2.lower()])) + result = self.game.recipes.get(key) + + if result is not None: + self._last_result = result + if self.material_mode == "unlimited": + self.inventory[result] = 1 + else: + self.inventory[result] = self.inventory.get(result, 0) + 1 + self.discovered_recipes.append((elem1, elem2, result)) + else: + self.failed_combinations.append((elem1, elem2)) + + return self._finish_step() + + def _finish_step(self) -> Tuple[str, float, bool, bool, Dict[str, Any]]: + self._success = self.game.target in self.inventory + terminated = self._success + truncated = ( + not terminated + and (self.current_round >= self.max_rounds or not self.get_feasible_actions()) + ) + reward = 1.0 if self._success else 0.0 + return self.render(), reward, terminated, truncated, self._get_info() + + def get_feasible_actions(self) -> List[Tuple[str, str]]: + elems = sorted(self.inventory.keys()) + pairs = [] + for i, e1 in enumerate(elems): + for e2 in elems[i:]: + if self.material_mode == "unlimited": + # Unlimited: any pair is feasible as long as elements exist + pairs.append((e1, e2)) + else: + if e1 == e2: + if self.inventory[e1] >= 2: + pairs.append((e1, e2)) + else: + pairs.append((e1, e2)) + return pairs + + def render(self) -> str: + inv_str = ", ".join(f"{e} x{c}" for e, c in sorted(self.inventory.items())) + lines = [ + f"Target: {self.game.target}", + f"Round: {self.current_round}/{self.max_rounds}", + f"Inventory: {inv_str}" if inv_str else "Inventory: (empty)", + ] + if self.discovered_recipes: + lines.append("Discovered recipes:") + for e1, e2, r in self.discovered_recipes: + lines.append(f" {e1} + {e2} = {r}") + if self.failed_combinations: + lines.append("Failed combinations:") + for e1, e2 in self.failed_combinations: + lines.append(f" {e1} + {e2}") + return "\n".join(lines) + + def _get_info(self) -> Dict[str, Any]: + return { + "inventory": dict(self.inventory), + "discovered_recipes": list(self.discovered_recipes), + "failed_combinations": list(self.failed_combinations), + "feasible_actions_count": len(self.get_feasible_actions()), + "target": self.game.target, + "current_round": self.current_round, + "success": self._success, + "last_result": self._last_result, + "invalid_action": self._invalid_action, + } diff --git a/trinity/common/workflows/connect_the_dots/alchemy/prompts/__init__.py b/trinity/common/workflows/connect_the_dots/alchemy/prompts/__init__.py new file mode 100644 index 00000000000..ea26a9597c8 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/alchemy/prompts/__init__.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +"""Prompt management for CoD Alchemy workflow using Jinja2 templates.""" + +from pathlib import Path +from typing import Dict, Optional + +from jinja2 import Environment, FileSystemLoader + +PROMPTS_DIR = Path(__file__).parent + + +def get_jinja_env() -> Environment: + return Environment( + loader=FileSystemLoader(PROMPTS_DIR), + trim_blocks=True, + lstrip_blocks=True, + ) + + +def load_system_prompt(**kwargs) -> str: + env = get_jinja_env() + template = env.get_template("system.jinja2") + return template.render(**kwargs) + + +def load_user_prompt( + current_round: int, + max_rounds: int, + inventory: Dict[str, int], + target: str, + action_feedback: Optional[str] = None, + **kwargs, +) -> str: + env = get_jinja_env() + template = env.get_template("user.jinja2") + return template.render( + current_round=current_round, + max_rounds=max_rounds, + inventory=inventory, + target=target, + action_feedback=action_feedback, + **kwargs, + ) diff --git a/trinity/common/workflows/connect_the_dots/alchemy/prompts/system.jinja2 b/trinity/common/workflows/connect_the_dots/alchemy/prompts/system.jinja2 new file mode 100644 index 00000000000..99d82e4eb06 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/alchemy/prompts/system.jinja2 @@ -0,0 +1,61 @@ +You are in an Alchemy crafting environment. Your goal is to synthesize the target element "{{ target }}" by combining elements in your inventory. + +## Game Mechanics + +There exists a fixed set of recipes. Each recipe takes exactly two input elements and produces one output element. A recipe's two inputs can be the same or different elements, and can be from different tiers. + +- **Combination**: Each round, choose two elements from your inventory to combine. If the pair matches a valid recipe, the result is added to your inventory. If not, nothing is produced. +- **Tier system**: Every element has a tier. Every recipe produces a result whose tier is strictly higher than both inputs' tiers. +{% if material_mode == "unlimited" %} +- **No consumption**: Elements are NOT consumed when used. You keep all elements after each attempt, whether it succeeds or fails. Newly discovered elements are permanently added to your inventory. +{% else %} +- **Consumption**: Every attempt consumes 1 copy of each input element, whether it succeeds or fails. If both inputs are the same element, 2 copies are consumed. Failed attempts lose materials with no result. +{% endif %} +- **Winning**: You win when "{{ target }}" appears in your inventory. +{% if material_mode == "unlimited" %} +- **Ending**: The game ends when you succeed or reach the round limit. +{% else %} +- **Ending**: The game ends when you succeed, run out of materials, or reach the round limit. +{% endif %} +{% if obfuscate_names %} +- All element names in this Alchemy crafting environment have no correspondence to real-world concepts. Do not try to guess recipes based on element names. +{% endif %} +{% if not show_recipes %} + +## Hidden Clues About the Environment +Recipes (i.e., which elements can be combined and which cannot) are unknown initially. You can try to discover them through experimentation, or based on additional hints provided in the context.{% if not obfuscate_names %} You may also use your knowledge of how elements naturally combine to make educated guesses.{% endif %} + +{% endif %} +{% if show_recipes %} + +## Available Recipes +{% for key, result in recipes.items() %} +- {{ key[0] }} + {{ key[1] }} = {{ result }} +{% endfor %} +{% endif %} +{% if show_elements %} + +## Elements in This World +{{ all_elements | join(', ') }} +{% endif %} +{% if show_tiers %} + +## Element Tiers + +{{ tier_info }} +{% endif %} + +## Response Format +First think about which combination of elements to try out in the current round, then provide your action using ... tags at the end. Format your complete response as follows: +``` +[THINKING] +Element1 + Element2 +``` +where [THINKING] should be replaced with your thinking process, while Element1 and Element2 are elements (same or different) chosen from your inventory. + +Other requirements: +- Be concise and avoid overthinking in your thinking process. +- Take one and only one action for each round. +- You should synthesize the target element within a limited number of rounds. Try to find a short path towards the goal, e.g., by leveraging recipes that have been revealed and avoiding combinations that are known to fail. +- Balance exploration and exploitation: as you strive to reach the goal, you may acquire information about the environment along the way, which can be potentially helpful for solving other similar tasks later on. + diff --git a/trinity/common/workflows/connect_the_dots/alchemy/prompts/user.jinja2 b/trinity/common/workflows/connect_the_dots/alchemy/prompts/user.jinja2 new file mode 100644 index 00000000000..b984b827405 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/alchemy/prompts/user.jinja2 @@ -0,0 +1,22 @@ +Round {{ current_round }}/{{ max_rounds }} +{% if action_feedback %} + +{{ action_feedback }} +{% endif %} + +Target: {{ target }} +{% if material_mode == "unlimited" %} + +Available elements in your inventory: +{% for elem in inventory.keys() | sort %} +- {{ elem }} +{% endfor %} +{% else %} + +Inventory ({{ inventory.values() | sum }} total copies): +{% for elem, count in inventory.items() | sort %} +- {{ elem }} x{{ count }} +{% endfor %} +{% endif %} + +Choose two elements available in your inventory to combine. diff --git a/trinity/common/workflows/connect_the_dots/alchemy/random_graph.py b/trinity/common/workflows/connect_the_dots/alchemy/random_graph.py new file mode 100644 index 00000000000..e7b92d1ad77 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/alchemy/random_graph.py @@ -0,0 +1,432 @@ +# -*- coding: utf-8 -*- +"""Procedural graph generator for Random Alchemy game. + +Generates DAG-structured recipe graphs from scratch without external data. +Each graph has tiered elements, configurable branching, cross-tier connections, +and noise (distractor) elements. +""" + +import random +import string +from collections import defaultdict +from typing import Dict, List, Optional, Set, Tuple + +from trinity.common.workflows.connect_the_dots.alchemy.common import ( + GameInstance, + RecipeKey, +) + + +def _generate_name(rng: random.Random, existing: Set[str], length_min: int = 3, length_max: int = 5) -> str: + """Generate a unique random element name (lowercase to match env recipe lookup).""" + while True: + length = rng.randint(length_min, length_max) + name = "".join(rng.choices(string.ascii_lowercase, k=length)) + if name not in existing: + return name + + +class RandomGraphGenerator: + """Procedural DAG generator for alchemy-style games. + + Generates a complete recipe graph with: + - Tiered elements (tier 0 = base, higher = synthesized) + - Configurable branching and cross-tier connections + - Noise (distractor) elements that are dead ends + """ + + def generate_graph( + self, + rng: random.Random, + num_tiers: int, + base_elements: int, + tier_shrink: float, + min_recipes_per_element: int, + max_recipes_per_element: int, + max_tier_gap: int, + cross_tier_prob: float, + noise_node_ratio: float, + noise_chain_depth: int, + ) -> Tuple[Dict[int, List[str]], Dict[RecipeKey, str], Dict[str, int], Set[str]]: + """Generate a complete recipe graph. + + Returns: + (tier_elements, recipes, local_tiers, noise_elements) + - tier_elements: {tier: [element_names]} (includes noise) + - recipes: {(e1, e2): result} + - local_tiers: {element: tier} + - noise_elements: set of element names that are distractors + """ + all_names: Set[str] = set() + + # Step 1: Determine elements per tier + tier_sizes = [base_elements] + for t in range(1, num_tiers): + size = max(1, round(tier_sizes[t - 1] * tier_shrink)) + tier_sizes.append(size) + + # Step 2: Generate element names per tier + tier_elements: Dict[int, List[str]] = {} + for t in range(num_tiers): + tier_elements[t] = [] + for _ in range(tier_sizes[t]): + name = _generate_name(rng, all_names) + all_names.add(name) + tier_elements[t].append(name) + + # Step 3: Generate recipes for each non-leaf element + recipes: Dict[RecipeKey, str] = {} + used_keys: Set[RecipeKey] = set() + + for t in range(1, num_tiers): + for elem in tier_elements[t]: + num_recipes = rng.randint(min_recipes_per_element, max_recipes_per_element) + for _ in range(num_recipes): + key = self._make_recipe( + rng, elem, t, tier_elements, max_tier_gap, + cross_tier_prob, used_keys, + ) + if key is not None: + recipes[key] = elem + used_keys.add(key) + + # Step 4: Add noise elements and recipes + noise_elements: Set[str] = set() + if noise_node_ratio > 0: + noise_elements = self._add_noise( + rng, tier_elements, recipes, used_keys, all_names, + noise_node_ratio, noise_chain_depth, max_tier_gap, cross_tier_prob, + ) + + # Step 5: Compute local tiers + local_tiers: Dict[str, int] = {} + for t, elems in tier_elements.items(): + for e in elems: + local_tiers[e] = t + + return tier_elements, recipes, local_tiers, noise_elements + + def _make_recipe( + self, + rng: random.Random, + result_elem: str, + result_tier: int, + tier_elements: Dict[int, List[str]], + max_tier_gap: int, + cross_tier_prob: float, + used_keys: Set[RecipeKey], + max_attempts: int = 50, + ) -> Optional[RecipeKey]: + """Generate one recipe for a given result element. + + - input1: always from tier T-1 + - input2: from tier T-1 (normal) or lower tier (cross-tier, with probability) + """ + for _ in range(max_attempts): + # input1: must be from tier T-1 + input1 = rng.choice(tier_elements[result_tier - 1]) + + # input2: cross-tier or same tier + if rng.random() < cross_tier_prob and result_tier >= 2: + min_tier = max(0, result_tier - max_tier_gap) + max_input_tier = result_tier - 2 # must be lower than T-1 + if min_tier <= max_input_tier: + input2_tier = rng.randint(min_tier, max_input_tier) + input2 = rng.choice(tier_elements[input2_tier]) + else: + input2 = rng.choice(tier_elements[result_tier - 1]) + else: + input2 = rng.choice(tier_elements[result_tier - 1]) + + key: RecipeKey = tuple(sorted([input1, input2])) + if key not in used_keys: + return key + + return None # Could not find a unique recipe + + def _add_noise( + self, + rng: random.Random, + tier_elements: Dict[int, List[str]], + recipes: Dict[RecipeKey, str], + used_keys: Set[RecipeKey], + all_names: Set[str], + noise_node_ratio: float, + noise_chain_depth: int, + max_tier_gap: int, + cross_tier_prob: float, + ) -> Set[str]: + """Add noise (distractor) elements that are dead ends. + + Returns the set of all noise element names. + """ + num_tiers = len(tier_elements) + # Snapshot original sizes before adding noise + original_sizes = {t: len(elems) for t, elems in tier_elements.items()} + all_noise: Set[str] = set() + + for depth_round in range(noise_chain_depth): + new_noise: Dict[int, List[str]] = {} + + for t in range(1, num_tiers): + # Number of noise nodes based on ORIGINAL tier size, not inflated + num_noise = max(0, round(original_sizes[t] * noise_node_ratio)) + if depth_round > 0: + # Fewer noise nodes in deeper rounds + num_noise = max(0, round(num_noise * 0.5)) + + for _ in range(num_noise): + noise_name = _generate_name(rng, all_names) + all_names.add(noise_name) + + # Generate one recipe for this noise node + key = self._make_recipe( + rng, noise_name, t, tier_elements, max_tier_gap, + cross_tier_prob, used_keys, + ) + if key is not None: + recipes[key] = noise_name + used_keys.add(key) + new_noise.setdefault(t, []).append(noise_name) + all_noise.add(noise_name) + + # Add noise nodes to tier_elements for next round + for t, names in new_noise.items(): + tier_elements[t].extend(names) + + return all_noise + + def generate_instance( + self, + seed: int, + min_num_tiers: int = 3, + max_num_tiers: int = 4, + min_base_elements: int = 10, + max_base_elements: int = 15, + tier_shrink_min: float = 0.4, + tier_shrink_max: float = 0.6, + min_recipes_per_element: int = 2, + max_recipes_per_element: int = 3, + max_tier_gap: int = 2, + min_cross_tier_prob: float = 0.2, + max_cross_tier_prob: float = 0.35, + min_noise_node_ratio: float = 0.15, + max_noise_node_ratio: float = 0.25, + noise_chain_depth: int = 3, + material_mode: str = "unlimited", + min_material_mult: float = 1.5, + max_material_mult: float = 2.0, + ) -> GameInstance: + """Generate a game instance for per-task scope.""" + rng = random.Random(seed) + + num_tiers = rng.randint(min_num_tiers, max_num_tiers) + base_elements = rng.randint(min_base_elements, max_base_elements) + tier_shrink = rng.uniform(tier_shrink_min, tier_shrink_max) + cross_tier_prob = rng.uniform(min_cross_tier_prob, max_cross_tier_prob) + noise_node_ratio = rng.uniform(min_noise_node_ratio, max_noise_node_ratio) + + tier_elements, recipes, local_tiers, noise_elements = self.generate_graph( + rng, num_tiers, base_elements, tier_shrink, + min_recipes_per_element, max_recipes_per_element, + max_tier_gap, cross_tier_prob, noise_node_ratio, noise_chain_depth, + ) + + # Target from highest tier (noise excluded) + valid_targets = [e for e in tier_elements[num_tiers - 1] if e not in noise_elements] + if not valid_targets: + # Fallback: try lower tiers + for t in range(num_tiers - 2, 0, -1): + valid_targets = [e for e in tier_elements[t] if e not in noise_elements] + if valid_targets: + break + target = rng.choice(valid_targets) + + return self._build_instance( + rng, target, tier_elements, recipes, local_tiers, + material_mode, min_material_mult, max_material_mult, + ) + + def generate_instance_for_pack( + self, + pack_seed: int, + task_seed: int, + task_idx: int = 0, + num_tasks: int = 8, + min_num_tiers: int = 3, + max_num_tiers: int = 4, + min_base_elements: int = 10, + max_base_elements: int = 15, + tier_shrink_min: float = 0.4, + tier_shrink_max: float = 0.6, + min_recipes_per_element: int = 2, + max_recipes_per_element: int = 3, + max_tier_gap: int = 2, + min_cross_tier_prob: float = 0.2, + max_cross_tier_prob: float = 0.35, + min_noise_node_ratio: float = 0.15, + max_noise_node_ratio: float = 0.25, + noise_chain_depth: int = 3, + material_mode: str = "unlimited", + min_material_mult: float = 1.5, + max_material_mult: float = 2.0, + ) -> GameInstance: + """Generate a game instance for per-pack scope. + + All tasks in a pack share the same graph. Each task gets a different target + selected via weighted sampling (higher tiers more likely, no duplicates). + """ + pack_rng = random.Random(pack_seed) + + num_tiers = pack_rng.randint(min_num_tiers, max_num_tiers) + base_elements = pack_rng.randint(min_base_elements, max_base_elements) + tier_shrink = pack_rng.uniform(tier_shrink_min, tier_shrink_max) + cross_tier_prob = pack_rng.uniform(min_cross_tier_prob, max_cross_tier_prob) + noise_node_ratio = pack_rng.uniform(min_noise_node_ratio, max_noise_node_ratio) + + tier_elements, recipes, local_tiers, noise_elements = self.generate_graph( + pack_rng, num_tiers, base_elements, tier_shrink, + min_recipes_per_element, max_recipes_per_element, + max_tier_gap, cross_tier_prob, noise_node_ratio, noise_chain_depth, + ) + + # Group valid (non-noise) elements by tier for target selection + tier_groups: Dict[int, List[str]] = {} + for t in range(1, num_tiers): + candidates = [e for e in tier_elements[t] if e not in noise_elements] + if candidates: + tier_groups[t] = candidates + + # Tier-weighted target selection, shuffled across tasks + ordered_targets = self._select_targets(pack_rng, tier_groups, num_tasks) + + target = ordered_targets[task_idx % len(ordered_targets)] + + task_rng = random.Random(task_seed) + return self._build_instance( + task_rng, target, tier_elements, recipes, local_tiers, + material_mode, min_material_mult, max_material_mult, + ) + + def _select_targets( + self, + rng: random.Random, + tier_groups: Dict[int, List[str]], + num_tasks: int, + ) -> List[str]: + """Select targets via tier-weighted sampling without replacement. + + First selects a tier (weight = tier + 1), then picks a random element + from that tier. Skips tiers that are exhausted. + """ + # Deep copy to avoid mutating + available = {t: list(elems) for t, elems in tier_groups.items()} + ordered: List[str] = [] + + for _ in range(num_tasks): + # Filter exhausted tiers + active = {t: elems for t, elems in available.items() if elems} + if not active: + break + + # Weighted tier selection + tiers = list(active.keys()) + weights = [t + 1 for t in tiers] + chosen_tier = rng.choices(tiers, weights=weights, k=1)[0] + + # Pick random element from chosen tier, remove it + elem = rng.choice(active[chosen_tier]) + available[chosen_tier].remove(elem) + ordered.append(elem) + + rng.shuffle(ordered) + return ordered + + def _build_instance( + self, + rng: random.Random, + target: str, + tier_elements: Dict[int, List[str]], + recipes: Dict[RecipeKey, str], + local_tiers: Dict[str, int], + material_mode: str, + min_material_mult: float, + max_material_mult: float, + ) -> GameInstance: + """Build a GameInstance for a given target.""" + all_elements = set() + for elems in tier_elements.values(): + all_elements.update(elems) + + # Trace one solution path + solution_path = self._trace_solution(target, recipes) + + # Starting inventory: all tier 0 elements, same quantity each + if material_mode == "unlimited": + starting_inventory = {e: 1 for e in tier_elements[0]} + else: + # Uniform count based on solution path needs + base_counts = self._compute_base_counts(solution_path) + max_needed = max(base_counts.values()) if base_counts else 1 + material_mult = rng.uniform(min_material_mult, max_material_mult) + uniform_count = max(max_needed, int(max_needed * material_mult + 0.5)) + starting_inventory = {e: uniform_count for e in tier_elements[0]} + + target_tier = local_tiers.get(target, 0) + + return GameInstance( + target=target, + recipes=recipes, + starting_inventory=starting_inventory, + all_elements=all_elements, + solution_path=solution_path, + depth=target_tier, + local_tiers=local_tiers, + ) + + @staticmethod + def _trace_solution( + target: str, + recipes: Dict[RecipeKey, str], + ) -> List[Tuple[str, str, str]]: + """Trace one solution path from base elements to target.""" + reverse: Dict[str, List[RecipeKey]] = defaultdict(list) + for key, result in recipes.items(): + reverse[result].append(key) + + path: List[Tuple[str, str, str]] = [] + visited: Set[str] = set() + + def _expand(elem: str): + if elem in visited: + return + visited.add(elem) + available = reverse.get(elem, []) + if not available: + return + chosen = available[0] + e1, e2 = chosen + _expand(e1) + _expand(e2) + path.append((e1, e2, elem)) + + _expand(target) + return path + + @staticmethod + def _compute_base_counts( + solution_path: List[Tuple[str, str, str]], + ) -> Dict[str, int]: + """Compute minimum base material counts for the solution path.""" + counts: Dict[str, int] = defaultdict(int) + available: Dict[str, int] = defaultdict(int) + + for e1, e2, result in solution_path: + for elem in [e1, e2]: + if available.get(elem, 0) > 0: + available[elem] -= 1 + else: + counts[elem] += 1 + available[result] = available.get(result, 0) + 1 + + return dict(counts) diff --git a/trinity/common/workflows/connect_the_dots/alchemy/workflow_random.py b/trinity/common/workflows/connect_the_dots/alchemy/workflow_random.py new file mode 100644 index 00000000000..c3be26b2cae --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/alchemy/workflow_random.py @@ -0,0 +1,313 @@ +# -*- coding: utf-8 -*- +""" +CoD workflow for Random Alchemy crafting game. + +Uses procedurally generated recipe graphs instead of Little Alchemy data. +Element names are already random, so no obfuscation needed. +""" + +from typing import List, Optional, Tuple + +from trinity.common.experience import Experience +from trinity.common.models.model import ModelWrapper +from trinity.common.workflows.connect_the_dots.alchemy.env import AlchemyEnv +from trinity.common.workflows.connect_the_dots.alchemy.prompts import ( + load_system_prompt, + load_user_prompt, +) +from trinity.common.workflows.connect_the_dots.alchemy.random_graph import ( + RandomGraphGenerator, +) +from trinity.common.workflows.connect_the_dots.base_workflow import ( + AsyncCoDMultiStepWorkflow, +) +from trinity.common.workflows.connect_the_dots.utils import extract_content_between_keys +from trinity.common.workflows.workflow import Task + + +def parse_action(response: str) -> Optional[Tuple[str, str]]: + """Parse action from model response.""" + key_start, key_end = "", "" + content, success = extract_content_between_keys(response, key_start, key_end) + if not success: + return None + action_str = content.strip() + parts = action_str.split("+") + if len(parts) != 2: + return None + elem1 = parts[0].strip() + elem2 = parts[1].strip() + if not elem1 or not elem2: + return None + return (elem1, elem2) + + +class CoDRandomAlchemyWorkflow(AsyncCoDMultiStepWorkflow): + """CoD Random Alchemy workflow using procedurally generated graphs.""" + + is_async: bool = True + + def __init__( + self, + model: ModelWrapper, + task: Task, + auxiliary_models: Optional[List] = None, + use_openai_client: bool = False, + ): + super().__init__( + task=task, + model=model, + auxiliary_models=auxiliary_models, + use_openai_client=use_openai_client, + ) + + workflow_args = task.workflow_args if hasattr(task, "workflow_args") else {} + self.max_rounds = workflow_args.get("max_rounds", 15) + self.max_response_tokens_restraint = workflow_args.get( + "max_response_tokens_restraint", None + ) + self.show_recipes = workflow_args.get("show_recipes", False) + self.show_elements = workflow_args.get("show_elements", False) + self.show_tiers = workflow_args.get("show_tiers", True) + self.material_mode = workflow_args.get("material_mode", "unlimited") + self.scope = workflow_args.get("scope", "per_task") + + # Graph generation params + self.min_num_tiers = workflow_args.get("min_num_tiers", 3) + self.max_num_tiers = workflow_args.get("max_num_tiers", 4) + self.min_base_elements = workflow_args.get("min_base_elements", 10) + self.max_base_elements = workflow_args.get("max_base_elements", 15) + self.tier_shrink_min = workflow_args.get("tier_shrink_min", 0.4) + self.tier_shrink_max = workflow_args.get("tier_shrink_max", 0.6) + self.min_recipes_per_element = workflow_args.get("min_recipes_per_element", 2) + self.max_recipes_per_element = workflow_args.get("max_recipes_per_element", 3) + self.max_tier_gap = workflow_args.get("max_tier_gap", 2) + self.min_cross_tier_prob = workflow_args.get("min_cross_tier_prob", 0.2) + self.max_cross_tier_prob = workflow_args.get("max_cross_tier_prob", 0.35) + self.min_noise_node_ratio = workflow_args.get("min_noise_node_ratio", 0.15) + self.max_noise_node_ratio = workflow_args.get("max_noise_node_ratio", 0.25) + self.noise_chain_depth = workflow_args.get("noise_chain_depth", 3) + self.min_material_mult = workflow_args.get("min_material_mult", 1.5) + self.max_material_mult = workflow_args.get("max_material_mult", 2.0) + + # Raw task params + self.raw_task = task.raw_task if hasattr(task, "raw_task") else {} + self.seed = self.raw_task.get("seed", 42) + + # Generate game instance + gen = RandomGraphGenerator() + gen_kwargs = dict( + min_num_tiers=self.min_num_tiers, + max_num_tiers=self.max_num_tiers, + min_base_elements=self.min_base_elements, + max_base_elements=self.max_base_elements, + tier_shrink_min=self.tier_shrink_min, + tier_shrink_max=self.tier_shrink_max, + min_recipes_per_element=self.min_recipes_per_element, + max_recipes_per_element=self.max_recipes_per_element, + max_tier_gap=self.max_tier_gap, + min_cross_tier_prob=self.min_cross_tier_prob, + max_cross_tier_prob=self.max_cross_tier_prob, + min_noise_node_ratio=self.min_noise_node_ratio, + max_noise_node_ratio=self.max_noise_node_ratio, + noise_chain_depth=self.noise_chain_depth, + material_mode=self.material_mode, + min_material_mult=self.min_material_mult, + max_material_mult=self.max_material_mult, + ) + + if self.scope == "per_pack": + pack_seed = self.raw_task.get("pack_seed", self.seed) + task_idx = self.raw_task.get("task_idx", 0) + num_tasks = self.raw_task.get("pack_size", 8) + self.game_instance = gen.generate_instance_for_pack( + pack_seed=pack_seed, + task_seed=self.seed, + task_idx=task_idx, + num_tasks=num_tasks, + **gen_kwargs, + ) + else: + self.game_instance = gen.generate_instance( + seed=self.seed, + **gen_kwargs, + ) + + # Create environment + self.env = AlchemyEnv(self.game_instance, max_rounds=self.max_rounds, + material_mode=self.material_mode) + + # State + self.done: bool = False + self.final_reward: float = 0.0 + self.current_step: int = 0 + self.action_feedback: Optional[str] = None + self.early_termination_by_format_issue: bool = False + + def _build_system_prompt(self) -> str: + target = self.game_instance.target + + kwargs = { + "show_recipes": self.show_recipes, + "show_elements": self.show_elements, + "show_tiers": self.show_tiers, + "obfuscate_names": True, # names are random, same prompt style + "material_mode": self.material_mode, + "target": target, + } + if self.show_recipes: + kwargs["recipes"] = self.game_instance.recipes + if self.show_elements: + kwargs["all_elements"] = sorted(self.game_instance.all_elements) + if self.show_tiers: + kwargs["tier_info"] = self._build_tier_info() + + sys_prompt = load_system_prompt(**kwargs) + + sys_prompt = self._augment_system_prompt(sys_prompt) + return sys_prompt + + def _build_tier_info(self) -> str: + local_tiers = self.game_instance.local_tiers or {} + tier_groups: dict = {} + for elem, tier in sorted(local_tiers.items(), key=lambda x: x[1]): + tier_groups.setdefault(tier, []).append(elem) + + lines = [] + for tier in sorted(tier_groups.keys()): + elems = tier_groups[tier] + # label = f"tier {tier}" if tier == 0 else f"tier {tier} (needs {tier} synthesis step{'s' if tier > 1 else ''})" + label = f"tier {tier}" + lines.append(f"- {label}: {', '.join(elems)}") + target = self.game_instance.target + target_tier = local_tiers.get(target, "?") + lines.append(f"\nThe target element '{target}' is at tier {target_tier}.") + return "\n".join(lines) + + def _build_action_feedback( + self, + action: Tuple[str, str], + result: Optional[str], + invalid: bool, + ) -> str: + e1, e2 = action + if invalid: + inv = self.env.inventory + if e1 not in inv and e2 not in inv: + return f"'{e1}' and '{e2}' are not in your inventory." + elif e1 not in inv: + return f"'{e1}' is not in your inventory." + elif e2 not in inv: + return f"'{e2}' is not in your inventory." + elif e1 == e2: + return f"Not enough '{e1}' (need 2, have {inv.get(e1, 0)})." + return f"Not enough materials for '{e1}' + '{e2}'." + if result is not None: + return f"You combined {e1} + {e2} and created: {result}!" + if self.material_mode == "unlimited": + return f"{e1} + {e2} — nothing happened." + return f"{e1} + {e2} — nothing happened. Materials consumed." + + async def run_async(self) -> List[Experience]: + obs, info = self.env.reset() + self.done = False + self.final_reward = 0.0 + self.current_step = 0 + self.action_feedback = None + self.early_termination_by_format_issue = False + + self.memory.clear() + self.memory.append({"role": "system", "content": self._build_system_prompt()}) + + return await super().run_async() + + async def step_async(self, step_num: int) -> Tuple[bool, List[Experience]]: + if self.done: + return False, [] + + target = self.game_instance.target + inv = self.env.inventory + + user_content = load_user_prompt( + current_round=step_num + 1, + max_rounds=self.max_rounds, + inventory=inv, + target=target, + action_feedback=self.action_feedback, + material_mode=self.material_mode, + ) + + if self.icl_examples and step_num == 0: + user_content = f"{user_content}\n\nHere are some reference examples:\n\n{self.icl_examples}" + + self.memory.append({"role": "user", "content": user_content}) + + if self.reply_prefix: + self.memory.append({"role": "assistant", "content": self.reply_prefix}) + + experiences = await self.model.chat_async(self.memory) + response_text = experiences[0].response_text + self.memory.append({"role": "assistant", "content": response_text}) + + sys_prompt = self.memory[0]["content"] if self.memory and self.memory[0]["role"] == "system" else "" + for exp in experiences: + exp.info["sys_prompt"] = sys_prompt + exp.info["user_prompt"] = user_content + + action = parse_action(response_text) + + if action is None: + self.action_feedback = ( + "Invalid format: could not parse combination. " + "Expected format: {your reasoning process here}Element1 + Element2. Game over." + ) + self.early_termination_by_format_issue = True + self.done = True + self.current_step = step_num + 1 + return False, experiences + + e1, e2 = action + + # Case-insensitive match to inventory + inv_lower = {x.lower(): x for x in self.env.inventory} + e1_canonical = inv_lower.get(e1.lower(), e1) + e2_canonical = inv_lower.get(e2.lower(), e2) + + obs, reward, terminated, truncated, info = self.env.step( + (e1_canonical, e2_canonical) + ) + + self.action_feedback = self._build_action_feedback( + (e1_canonical, e2_canonical), + info.get("last_result"), + invalid=info.get("invalid_action", False), + ) + + self.done = terminated or truncated + self.current_step = step_num + 1 + + if terminated and reward > 0: + self.final_reward = reward + + return not self.done, experiences + + def _get_feedback(self) -> str: + target = self.game_instance.target + + if target in self.env.inventory: + return f"Success! Synthesized {target}." + if self.early_termination_by_format_issue: + return self.action_feedback + if len(self.env.get_feasible_actions()) == 0: + return ( + f"Failed: Ran out of materials. Could not synthesize {target}. " + f"Used {self.current_step} rounds." + ) + return ( + f"Failed: Ran out of rounds ({self.current_step}/{self.max_rounds}). " + f"Could not synthesize {target}." + ) + + @property + def max_step_num(self) -> int: + return self.max_rounds diff --git a/trinity/common/workflows/connect_the_dots/base_workflow.py b/trinity/common/workflows/connect_the_dots/base_workflow.py new file mode 100644 index 00000000000..6efefb7e7d1 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/base_workflow.py @@ -0,0 +1,304 @@ +# -*- coding: utf-8 -*- +""" +Base workflow class for CoD (Connect-the-Dots) multi-step workflows. + +This workflow is designed for use with CoDWorkflow, which handles metrics +post-processing via _post_process_task_solving_exp. Therefore, this base class +does NOT broadcast metrics to every experience (avoiding metric explosion like +mean@101, mean@102, etc. in eval logs). +""" + +import re +from dataclasses import asdict +from typing import Any, List, Optional, Tuple + +import openai + +from trinity.common.experience import Experience +from trinity.common.models.model import ModelWrapper +from trinity.common.workflows.connect_the_dots.agentscope_utils import ( + agentscope_msgs_to_text, +) +from trinity.common.workflows.connect_the_dots.utils import extract_content_between_keys +from trinity.common.workflows.workflow import Task, Workflow + + +class AsyncCoDMultiStepWorkflow(Workflow): + """ + Async base class for CoD multi-step workflows. + + This is the recommended base class for CoD multi-step workflows like + CoDFrozenLakeWorkflow, CoDAlfworldWorkflow, CoDTextWorldWorkflow, etc. + + Unlike RewardPropagationWorkflow, this class does NOT set metrics on every + experience. Metrics should be handled by CoDWorkflow._post_process_task_solving_exp. + + Subclasses should: + - Initialize self.memory as a list of message dicts in __init__ or reset + - The memory should follow the format: [system, user, assistant, user, assistant, ...] + """ + + is_async: bool = True + + def __init__( + self, *, task: Task, model: ModelWrapper, auxiliary_models=None, use_openai_client=True + ): + super().__init__(task=task, model=model, auxiliary_models=auxiliary_models) + self.client: Optional[openai.OpenAI] = None + if use_openai_client: + self.client = model.get_openai_client() + + # Memory for conversation history (subclasses should populate this) + self.memory: List[dict] = [] + + # State variables (TODO: merge with those in children classes) + self.final_reward: float = 0.0 # outcome reward + self.early_termination_by_format_issue: bool = False # terminate upon format error + self.reply_prefix = task.format_args.reply_prefix if task.format_args else None + self.hint = None + self.icl_examples = None + self.max_response_tokens_restraint = None + + def reset(self, task: Task): + """Set task-derived attrs. Subclasses override and call super().""" + self.task = task + self.format_args = task.format_args + self.raw_task = task.raw_task or {} + self.task_desc = task.task_desc + self.reply_prefix = task.format_args.reply_prefix if task.format_args else None + self.hint = None + self.icl_examples = None + self.max_response_tokens_restraint = None + + @property + def rollout_args(self): + return asdict(self.task.rollout_args) + + def _compress_assistant_response(self, response: str) -> str: + """Various methods of compressing assistant response to reduce context size.""" + context_compression_mode = self.task.workflow_args.get("context_compression_mode", "keep_all") + if context_compression_mode == "remove_think": + # TODO: need update, maybe return None when no match of tag + compressed = re.sub(r".*?", "", response, flags=re.DOTALL) + return compressed.strip() + elif context_compression_mode == "keep_answer": + content, ok = extract_content_between_keys(response, "", "") + if ok: + # re-wrap with tags to allow repeated compression + return f"{content.strip()}" + return "(Failed to parse answer from response)" + elif context_compression_mode == "keep_all": + return response + else: + raise ValueError(f"Invalid context_compression_mode {context_compression_mode}") + + def _compress_memory(self) -> None: + """Compress all assistant messages in memory by stripping blocks. + + Called after each step_async to prevent context bloat in multi-turn + conversations. The thinking/reasoning content is removed while + preserving answers and other non-thinking output. + + !!! TODO: change to compressing the last assistant message? !!! + """ + for msg in self.memory: + if msg.get("role") == "assistant": + msg["content"] = self._compress_assistant_response(msg["content"]) + + async def run_async(self) -> list[Experience]: + """Run the workflow asynchronously and return a list of experiences.""" + experiences = [] + step = 0 + for step in range(self.max_step_num): + # Run a single step of the agent application and get experiences directly + continue_run, exps = await self.step_async(step_num=step) + # Compress prior assistant messages in memory to avoid context bloat + self._compress_memory() + # Set the step number in each experience + for exp in exps: + exp.eid.step = step + # Store the step experiences + experiences.extend(exps) + if not continue_run: + break + + # Calculate final reward and propagate to all experiences + reward = await self.reward_async(experiences) + for exp in experiences: + exp.reward = reward + if exp.metrics is None: + exp.metrics = {} + + # Only set actual_env_steps and trajectory on the LAST experience + # (CoDWorkflow._post_process_task_solving_exp will handle per-experience metrics) + if experiences: + experiences[-1].metrics["actual_env_steps"] = step + 1 + experiences[-1].info["trajectory"] = self._build_trajectory() + + return experiences + + def _augment_system_prompt(self, sys_prompt: str) -> str: + """Augment system prompt, injecting hint and max_response_tokens_restraint.""" + if self.hint: + sys_prompt = sys_prompt + ( + "\n\n## Hints that might help\n" + "Below are some hints that might be helpful, but there is no guarantee " + "that they must be correct or applicable to the current task. You might " + "leverage them as prior knowledge, while incorporating new information " + "from your own experience in interacting with the environment.\n\n" + f"{self.hint}" + ) + if self.max_response_tokens_restraint: + sys_prompt = sys_prompt + ( + f"\n\n## Response length limit\n" + f"Please limit your response (including your thinking process) " + f"to {self.max_response_tokens_restraint} tokens." + ) + return sys_prompt + + @staticmethod + def _strip_system_prompt(sys_content: str) -> str: + """Strip hint and token limit from system prompt, keeping only task rules. + Markers correspond to those in the _augment_system_prompt method above. + """ + for marker in [ + "\n\n## Hints", + "\n\n## Response length limit", + ]: + sys_content = sys_content.split(marker)[0] + return sys_content + + @staticmethod + def _strip_icl_examples(user_content: str) -> str: + marker = "\n\nHere are some reference examples:\n\n" + if marker in user_content: + return user_content.split(marker)[0] + return user_content + + def _build_trajectory(self) -> str: + """Build trajectory string from memory for hint generation. + + Includes the system prompt (with hint/token limit stripped) so that + hint generation can see the task rules, followed by observation/action + pairs from the conversation history. + + Subclasses can override this method for custom trajectory formatting. + """ + if not self.memory: + return "" + + trajectory_parts = [] + step_num = 0 + i = 0 + while i < len(self.memory): + msg = self.memory[i] + # Include system prompt with hint/token limit stripped + if msg.get("role") == "system": + clean_sys = self._strip_system_prompt(msg.get("content", "")) + if clean_sys.strip(): + trajectory_parts.append(f"System:\n{clean_sys}") + i += 1 + continue + # Process user message + if msg.get("role") == "user": + step_num += 1 + observation = msg.get("content", "") + observation = self._strip_icl_examples(observation) + # Find the last assistant message before next user (to skip reply_prefix) + action = "(no action)" + j = i + 1 + while j < len(self.memory) and self.memory[j].get("role") == "assistant": + action = self.memory[j].get("content", "") + j += 1 + trajectory_parts.append(f"Step {step_num}:\nObservation: {observation}\nAction: {action}") + i = j + else: + i += 1 + + return "\n\n".join(trajectory_parts) + + def _build_agentscope_trajectory(self, sys_prompt: str, messages: List[Any]) -> str: + """Build CoD trajectory text from AgentScope memory messages.""" + trajectory_parts = [] + sys_prompt = self._strip_system_prompt(sys_prompt) + if sys_prompt.strip(): + trajectory_parts.append(f"System:\n{sys_prompt}") + + steps: List[Tuple[str, List[Any]]] = [] + + for msg in messages: + if msg.role == "user": + steps.append((msg.get_text_content() or "", [])) + elif steps: + steps[-1][1].append(msg) + + for step_num, (user_content, step_messages) in enumerate(steps, start=1): + observation = self._strip_icl_examples(user_content) + # !!! AgentScope stores tool calls/results as structured blocks. + action = agentscope_msgs_to_text(step_messages) + if not action: + action = "(no action)" + trajectory_parts.append( + f"Step {step_num}:\nObservation: {observation}\nAction: {action}" + ) + return "\n\n".join(trajectory_parts) + + async def step_async(self, step_num: int) -> Tuple[bool, List[Experience]]: + """Run a single step of your agent application asynchronously. + + Args: + step_num (int): The current step number. + + Returns: + Tuple[bool, List[Experience]]: A tuple of (continue_run, experiences). + - continue_run: Whether to continue running the agent application. + - experiences: List of experiences from this step. + """ + raise NotImplementedError + + def _get_feedback(self) -> str: + """Get environment feedback that will be added to exps[-1].info["feedback"]""" + raise NotImplementedError + + async def reward_async(self, exps: List[Experience]) -> float: + """Default reward function for CoD multi-step workflows.""" + if exps: + exps[-1].info["feedback"] = self._get_feedback() + if exps[-1].metrics is None: + exps[-1].metrics = {} + exps[-1].metrics["format_error_termination"] = 1.0 if self.early_termination_by_format_issue else 0.0 + exps[-1].info["early_termination_by_format_issue"] = self.early_termination_by_format_issue + + # Original outcome reward + returned_reward = self.final_reward + + # Length penalties + length_penalty_coef = self.task.workflow_args.get("length_penalty_coef", 0.0) + if exps and (length_penalty_coef > 0.0) and (self.final_reward > 0.0): + len_full_penalty = self.task.workflow_args.get("len_full_penalty", -1) + len_zero_penalty = self.task.workflow_args.get("len_zero_penalty", -1) + assert len_full_penalty > 0, "len_full_penalty should be set to a positive integer." + assert len_zero_penalty > 0, "len_zero_penalty should be set to a positive integer." + assert len_full_penalty > len_zero_penalty, "len_full_penalty should be larger than len_zero_penalty." + + # response-wise penalty + resp_len_penalties = [] + for exp in exps: + resp_len = len(exp.tokens) - exp.prompt_length + penalty = min(1.0, max(0.0, (resp_len - len_zero_penalty) / (len_full_penalty - len_zero_penalty))) + resp_len_penalties.append(penalty) + response_wise_penalty = sum(resp_len_penalties) / len(resp_len_penalties) # range [0, 1] + + # episode-wise penalty + episode_wise_penalty = len(exps) / self.max_step_num # range [0, 1] + + # combine and substract from original reward + total_penalty = length_penalty_coef * (response_wise_penalty + episode_wise_penalty) + returned_reward = max(0.0, returned_reward - total_penalty) + + return returned_reward + + @property + def max_step_num(self) -> int: + """Return the maximum number of steps in the task.""" + raise NotImplementedError diff --git a/trinity/common/workflows/connect_the_dots/cod_utils.py b/trinity/common/workflows/connect_the_dots/cod_utils.py new file mode 100644 index 00000000000..1225039fd58 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/cod_utils.py @@ -0,0 +1,190 @@ +"""Utils for CoD.""" + +from collections import defaultdict +from typing import Dict, List, Tuple + +from trinity.common.workflows.connect_the_dots.utils import extract_content_between_keys + + +def compute_part_summary(exps: List) -> dict: + """Compute summary statistics (accuracy per interaction step) for a list of experiences.""" + step_rewards: Dict[int, List[float]] = defaultdict(list) + all_rewards = [] + for exp in exps: + reward = exp.reward + all_rewards.append(reward) + step_id = exp.eid.step + if step_id is not None: + step_rewards[step_id].append(reward) + + num_samples = len(all_rewards) + overall_acc = sum(all_rewards) / num_samples if num_samples > 0 else 0.0 + + per_step_acc = {} + for sid in sorted(step_rewards.keys()): + rs = step_rewards[sid] + per_step_acc[str(sid)] = round(sum(rs) / len(rs), 4) + + return { + "overall_acc": round(overall_acc, 4), + "num_samples": num_samples, + "per_interaction_step_acc": per_step_acc, + } + + +def clone_model_with_isolated_history(model): + """Clone a model wrapper with isolated, enabled request history.""" + isolated_model = model.clone_with_isolated_history() + isolated_model.enable_history = True + return isolated_model + + +HINTS_EXAMPLE_PROMPT = """As an example, the end of a complete response might be: +I would retain the planning and verification lessons because they transfer to +future tasks, while omitting details specific to the completed task. +--- Start of updated hints --- +- Start with a list of known conditions. +- Create a high-level plan and verify each important step. +- Return the final answer only when sufficiently confident. +--- End of updated hints --- +""" + +HINT_START_MARKER = "--- Start of updated hints ---" +HINT_END_MARKER = "--- End of updated hints ---" + + +class CoDPrompts: + @staticmethod + def sys_prompt_gen_hint_iteratively(hint_example: bool = False) -> str: + """System prompt for iterative hint generation.""" + prompt = """You are a helpful assistant and a cross-task learning agent. Your responsibility is to solve **a sequence of different-but-related tasks** within the same environment, while **transferring informative hints across tasks** that can help achieve better task-solving performance. + +## Problem setting + +**In the current session, your job is to update the hints.** + +To be concrete, the user provides: +1. Previous hints that were used to guide task solving; +2. A new task-solving trajectory. + +Based on this information, you need to generate updated hints that can assist in solving future tasks within the same environment more effectively, by +- Preserving useful guidance and information from the previous hints, while discarding incorrect ones; +- Incorporating new lessons and information that can be learned from the new trajectory. + +## General principles + +- You are in an environment that might be stationary or non-stationary, and relation between tasks in this environment is initially unknown. One general principle is thus to generate **informative** hints that can serve as useful priors when solving a new task. If you are uncertain about the correctness or usefulness of certain hints, feel free to include them in your response and briefly mention uncertainty. +- There are at least two major categories of useful hints: (1) revealed information / clues about the environment that were initially unknown; (2) task-solving techniques with validated efficacy. With that said, your updated hints can certainly go beyond these two categories. + +## Response format + +First provide a brief thinking process that evaluates the previous hints and the +new trajectory. After that reasoning, finish your response with exactly this +hint block: + +--- Start of updated hints --- +- Your concise, transferable hints go here. +--- End of updated hints --- + +The two delimiter lines are literal protocol tokens. Copy both exactly, +including capitalization, spaces, and hyphens. + +Other requirements: +- Keep your thinking process concise. Avoid overthinking. +- Put the thinking process before the start delimiter, never inside the hint block. +- Do not place any text after the end delimiter. +- Keep your updated hints concise if possible, containing the most critical and helpful information. Avoid generic and uninformative statements. Avoid repeating the system prompt within the task-solving trajectory. +- Make sure that your updated hints can be potentially helpful for other related tasks, rather than specific to the task in the provided trajectory. +- Use standard Markdown format for your updated hints. Simpler structures (e.g., bullet points) are preferred. If you do need to use sections, you should start from the third level "###", while avoiding "#" and "##". +""" + if hint_example: + prompt = prompt + HINTS_EXAMPLE_PROMPT + return prompt + + @staticmethod + def user_prompt_gen_hint_iteratively( + prev_hint: str, + trajectory: str, + reward: float, + feedback: str, + ) -> str: + """User prompt for iterative hint generation. + + Args: + prev_hint: Previous hints (empty string if first iteration) + trajectory: The solution trajectory (includes task description and model responses) + reward: The reward received (e.g., 1.0 for success, 0.0 for failure) + feedback: Environment feedback (e.g., final state, error message) + """ + if prev_hint: + prev_hint_section = f"""--- Previous hints --- + +{prev_hint} + +""" + else: + prev_hint_section = """--- Previous hints --- + +(No previous hint available) + +""" + + prompt = f"""{prev_hint_section} +--- A new task-solving trajectory --- + +{trajectory} + +Reward: {reward:.3f} + +Environment feedback: {feedback} + +--- Your job --- + +Now, please think through it and return your updated hints based on the above information, following the requirements in the system prompt. +""" + # !!! For "cheating" in environments like frozenlake-obscure, add: + # "Note that there were initially some hidden clues about the environment. If you can reveal some of them based on the provided trajectory, include them in your updated hints." + + return prompt + + @staticmethod + def extract_hint(response: str) -> Tuple[str, bool]: + """Extract hints from response. + + Returns: extracted hint (str) and indicator of successful parsing (bool) + """ + content, success = extract_content_between_keys( + response, HINT_START_MARKER, HINT_END_MARKER + ) + if success and content.strip(): + return "Hints: " + content.strip(), True + return "Hints: no hint available.", False + + +# Inline warning prepended to per-trajectory ground-truth reference when +# the workflow surfaces it via the feedback string. Kept brief on purpose; +# do not enumerate transferability hints — let the system prompt drive that. +GT_LEAKAGE_WARNING = ( + "The ground-truth reference below is case-specific. " + "Updated hints will be applied to other cases, so abstract " + "transferable insights and do not copy case-specific facts." +) + + +def format_gt_block_for_feedback(gt_reference: str) -> str: + """Format a case-specific GT reference as a feedback suffix block. + + Workflows that want to surface a per-trajectory oracle signal to + the iterative hint generator should append this block to their + feedback string (via exp.info["feedback"]). + + Returns an empty string if `gt_reference` is empty, so callers can + unconditionally concatenate without dragging in a warning that + points at nothing. + """ + if not gt_reference: + return "" + return ( + f"\n\n{GT_LEAKAGE_WARNING}\n\n" + f"--- Ground truth reference (case-specific) ---\n{gt_reference}" + ) diff --git a/trinity/common/workflows/connect_the_dots/cod_workflow.py b/trinity/common/workflows/connect_the_dots/cod_workflow.py new file mode 100644 index 00000000000..ae2fa1fe802 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/cod_workflow.py @@ -0,0 +1,909 @@ +"""Connect-the-Dots (CoD) workflow for cross-task learning and generalization.""" + +import asyncio +import hashlib +import json +import os +from copy import deepcopy +from typing import Dict, List, Optional, Tuple, Union + +from trinity.common.experience import Experience +from trinity.common.models.model import ModelWrapper +from trinity.common.workflows.connect_the_dots.cod_utils import ( + clone_model_with_isolated_history, + compute_part_summary, +) +from trinity.common.workflows.connect_the_dots.update_context_workflow import ( + AsyncCoDUpdateContextWorkflow, + AsyncCoDUpdateContextAgentWorkflow, +) +from trinity.common.workflows.connect_the_dots.utils import compute_stable_pack_seed +from trinity.common.workflows.workflow import Task, Workflow + + +def compute_task_hash(task_desc: str) -> str: + """Compute hash of task description.""" + return hashlib.md5(task_desc.encode()).hexdigest()[:16] + + +def get_taskset_id(task: Task) -> int: + """Get taskset_id of a task. + + Ref: class TasksetScheduler (renamed to DatasetScheduler later), + task.index["taskset_id"] = taskset_id (type: int) + This is applicable only when there are > 1 tasksets. + """ + # return task.index["taskset_id"] + return task.index.get("taskset_id", 0) + + +def pack_tasks( + tasks: List[Task], + pack_size: int, + cod_workflow_args: dict, + pack_strategy: str, +) -> List[Task]: + """Group tasks by taskset_id, pack for each group, and merge outputs. + + For ``cod`` and ``ralph_sample``, drop a group if it has fewer than + ``pack_size`` tasks, or pad it cyclically when ``pad_tasks_to_full_pack`` + is enabled. ``ralph_full`` expands every task into a full pack and does + not drop or pad groups. + """ + if pack_size <= 0: + raise ValueError(f"pack_size should be positive, got {pack_size}.") + if pack_strategy not in ["cod", "ralph_sample", "ralph_full"]: + raise ValueError(f"Unsupported pack_strategy: {pack_strategy}") + + returned_tasks: List[Task] = [] + grouped_tasks = dict() # taskset_id -> list of original tasks + + # Group tasks by taskset_id + for task in tasks: + taskset_id = get_taskset_id(task) + if taskset_id not in grouped_tasks: + grouped_tasks[taskset_id] = [] + grouped_tasks[taskset_id].append(task) + + # Logs for debug + log_count_original = dict() + log_count_final = dict() + log_count_pack = dict() + + # Pack tasks for each group, and merge outputs + for id, task_lst in grouped_tasks.items(): + log_count_original[id] = len(task_lst) + + # ralph_full expands every real task into a pack, without drop-tail or padding. + if pack_strategy == "ralph_full": + log_count_final[id] = len(task_lst) + else: + if len(task_lst) < pack_size: + log_count_final[id] = 0 + log_count_pack[id] = 0 + continue # too few tasks, drop this group + + if cod_workflow_args.get("pad_tasks_to_full_pack", False): + remain_count = len(task_lst) % pack_size + if remain_count > 0: + pad_count = pack_size - remain_count + task_lst = task_lst + task_lst[:pad_count] # pad in a cyclic manner + log_count_final[id] = len(task_lst) + + packed_tasks = pack_tasks_from_same_taskset( + tasks=task_lst, + pack_size=pack_size, + cod_workflow_args=cod_workflow_args, + pack_strategy=pack_strategy, + ) + returned_tasks.extend(packed_tasks) + log_count_pack[id] = len(packed_tasks) + + for id in log_count_original.keys(): + print( + f"original task count {log_count_original[id]},", + f"final task count {log_count_final[id]},", + f"final pack count {log_count_pack[id]}.", + ) + + return returned_tasks + + +def pack_tasks_from_same_taskset( + tasks: List[Task], + pack_size: int, + cod_workflow_args: dict, + pack_strategy: str, +) -> List[Task]: + packed_tasks = [] + + num_packs = len(tasks) if pack_strategy == "ralph_full" else len(tasks) // pack_size + for i in range(num_packs): + if pack_strategy == "cod": + start, end = i * pack_size, (i + 1) * pack_size + sublst = tasks[start:end] + else: + # Ralph strategies repeat one anchor: every task for full, or the + # first task in each regular pack for sample. + anchor_idx = i if pack_strategy == "ralph_full" else i * pack_size + env_task_idx = i % pack_size if pack_strategy == "ralph_full" else 0 + anchor_task = tasks[anchor_idx] + sublst = [] + for _ in range(pack_size): + task = deepcopy(anchor_task) + task.index["env_task_idx"] = env_task_idx + sublst.append(task) + + new_task = deepcopy(sublst[0]) + new_task.workflow = CoDWorkflow + new_task.workflow_args = deepcopy(cod_workflow_args) + + if new_task.raw_task is None: + new_task.raw_task = dict() + new_task.raw_task["aux_tasks"] = sublst[1:] + new_task.raw_task["all_tasks"] = sublst + + packed_tasks.append(new_task) + + return packed_tasks + + +class CoDWorkflow(Workflow): + """A CoD "meta workflow" that can call an existing workflow's run method. + + Assumptions for current version: + - input task is a pack of multiple tasks in task.raw_task["all_tasks"] + - task-solving workflow is async and provides trajectory information + - reusable task-solving workflows set can_reset = True + """ + + can_reset: bool = True + can_repeat: bool = True + is_async: bool = True + + def __init__( + self, + *, + task: Task, + model: ModelWrapper, + auxiliary_models: Optional[List[ModelWrapper]] = None, + ): + super().__init__( + task=task, + model=model, + auxiliary_models=auxiliary_models, + ) + self.all_tasks: List[Task] = [] + self.all_workflow_instances: List[ + Union[Workflow, None] + ] = [] # one workflow instance for each task + self.update_context_workflow: Optional[Workflow] = None + + self.reset(task) + + def _model_for_workflow(self, workflow_cls): + """Return the model wrapper required by a child workflow.""" + # !!! CoD AgentScope workflows need isolated history to recover Experiences. + if getattr(workflow_cls, "requires_isolated_model_history", False): + return clone_model_with_isolated_history(self.model) + return self.model + + def reset(self, cod_task: Task): + """Reset current CoD workflow, as well as all task-solving workflow instances. + + (Doesn't matter much if we always use max_repeat_times_per_runner = 1) + """ + self.task = cod_task + + self.all_tasks = [] + if isinstance(cod_task.raw_task, dict) and "all_tasks" in cod_task.raw_task.keys(): + self.all_tasks = cod_task.raw_task["all_tasks"] # List[Task] + + # Optionally inject pack-level seed into sub-tasks (for per_pack mapping mode in FrozenLakeObscure, etc.) + # Enable via workflow_args: inject_pack_seed: true + if cod_task.workflow_args.get("inject_pack_seed", False): + if cod_task.workflow_args.get("stable_pack_seed", False): + # Stable dataset identities keep eval environments fixed across checkpoints. + pack_seed = compute_stable_pack_seed( + (get_taskset_id(task), task.index["index"]) + for task in self.all_tasks + ) + else: + pack_seed = hash((cod_task.batch_id, cod_task.task_id)) % (2**32) + for i, task in enumerate(self.all_tasks): + if task.raw_task is None: + task.raw_task = {} + task.raw_task["pack_seed"] = pack_seed + # Ralph packs repeat one real task; keep env task_idx fixed for per-pack mappings. + # especially for alchemy_random + task.raw_task["task_idx"] = task.index.get("env_task_idx", i) + task.raw_task["pack_size"] = len(self.all_tasks) + + # Init update-context workflow + taskset_workflow_args = ( + self.all_tasks[0].workflow_args if self.all_tasks else {} + ) + update_context_impl = taskset_workflow_args.get( + "update_context_impl", + cod_task.workflow_args.get("update_context_impl", "update_context"), + ) + if update_context_impl == "pde_update_context": + from .pde_discovery.update_context_workflow import ( + PDEUpdateContextWorkflow, + ) + + update_context_cls = PDEUpdateContextWorkflow + else: + update_context_cls = { + "update_context": AsyncCoDUpdateContextWorkflow, + "update_context_agent": AsyncCoDUpdateContextAgentWorkflow, + }[update_context_impl] + if ( + self.update_context_workflow is None + or self.update_context_workflow.__class__ != update_context_cls + ): + self.update_context_workflow = update_context_cls( + task=cod_task, + model=self._model_for_workflow(update_context_cls), + auxiliary_models=self.auxiliary_model_wrappers, + ) + else: + self.update_context_workflow.reset(cod_task) + + reuse_workflow_instance = cod_task.workflow_args["reuse_workflow_instance"] + if reuse_workflow_instance is False: + self.all_workflow_instances = [None for _ in range(len(self.all_tasks))] + return + + if len(self.all_workflow_instances) not in (0, len(self.all_tasks)): + self.all_workflow_instances = [] + + if len(self.all_workflow_instances) == 0: + for task in self.all_tasks: + workflow_instance = task.to_workflow( + self._model_for_workflow(task.workflow), + self.auxiliary_model_wrappers, + ) + self.all_workflow_instances.append(workflow_instance) + else: + for i, task in enumerate(self.all_tasks): + workflow_instance = self.all_workflow_instances[i] + # 类型一致且可以 reset 才复用 + if workflow_instance.__class__ == task.workflow and workflow_instance.can_reset: + workflow_instance.reset(task) + else: + self.all_workflow_instances[i] = task.to_workflow( + self._model_for_workflow(task.workflow), + self.auxiliary_model_wrappers, + ) + + def set_repeat_times(self, repeat_times, run_id_base): + assert ( + repeat_times == 1 + ), "Current CoD implementation requires repeat_times to be 1 here." + + self.repeat_times = repeat_times + self.task.rollout_args.n = repeat_times + self.run_id_base = run_id_base + + # Each solve-task workflow runs once per CoD trajectory. + for task in self.all_tasks: + task.rollout_args.n = 1 + for workflow_instance in self.all_workflow_instances: + if workflow_instance is not None and workflow_instance.can_repeat: + workflow_instance.set_repeat_times(1, run_id_base) + + # --- CoD utils --- + + @staticmethod + def _get_trajectory(task_exps: List[Experience]) -> str: + """Get trajectory string from experiences. + + Requires workflow to provide trajectory in exp.info["trajectory"]. + Raises KeyError if trajectory is not provided. + """ + if not task_exps: + raise ValueError("task_exps is empty, cannot get trajectory") + + last_exp = task_exps[-1] + if "trajectory" not in last_exp.info: + raise KeyError( + "Workflow must provide trajectory in exp.info['trajectory']. " + "For multi-step workflows, inherit from AsyncCoDMultiStepWorkflow. " + "For single-turn workflows, set trajectory in run_async() with user + assistant format." + ) + return last_exp.info["trajectory"] + + @staticmethod + def _get_feedback(task_exps: List[Experience], task_reward: Optional[float]) -> str: + """Get feedback string from experiences.""" + last_exp = task_exps[-1] if task_exps else None + if last_exp and "feedback" in last_exp.info: + feedback = last_exp.info["feedback"] + else: + # Fallback for workflows without feedback + assert task_reward is not None + feedback = f"Reward: {task_reward} (max: 1.0)" + return feedback + + def _post_process_hint_generation_exp( + self, + exp: Experience, + subtid: int, + part_id: int, + part_name: str, + repeat_id: int, + step_id: int, + ) -> None: + if exp.metrics is None: + exp.metrics = {} + hint = exp.info["hint"] + key_hint_len_with_suffix = f"hint_len_in_char_{part_name}" + exp.metrics.update( + { + "reward_gen_hint": exp.reward, + key_hint_len_with_suffix: len(hint), + } + ) + + exp.eid.task = "_".join( + [ + "batch", + str(self.task.batch_id), + "main", + str(self.task.task_id), + "sub", + str(subtid), + ] + ) + exp.eid.run = ( + self.run_id_base * 100 + self.repeat_times * part_id + repeat_id + ) # TODO: eliminate magic number 100 + exp.eid.step = step_id + + def _post_process_task_solving_exp( + self, + exp: Experience, + subtid: int, + part_id: int, + part_name: str, + repeat_id: int, + append_part_name_to_task_id: bool = False, + ) -> None: + # Only update metrics if exp.metrics is non-empty (i.e., the last exp in a trajectory) + # This avoids metric explosion (mean@101, mean@102, etc.) when using multi-step workflows + # !!! TODO: this prevents setting metrics in workflow, better change this if-condition to something more robust + # !!! TODO: another issue here is that response_len_in_char is only record for last exp of an episode. + if exp.metrics: + key_reward_with_suffix = f"reward_{part_name}" + key_response_len_with_suffix = f"response_len_in_char_{part_name}" + exp.metrics.update( + { + "reward": exp.reward, + key_reward_with_suffix: exp.reward, + key_response_len_with_suffix: len(exp.response_text), + } + ) + + # task / run / step id + task_id_pieces = [ + "batch", + str(self.task.batch_id), + "main", + str(self.task.task_id), + "sub", + str(subtid), + ] + if append_part_name_to_task_id: + # useful for contolling which exps to group in grpo-like advantage calculation + task_id_pieces.extend(["part", part_name]) + exp.eid.task = "_".join(task_id_pieces) + exp.eid.run = ( + self.run_id_base * 100 + self.repeat_times * part_id + repeat_id + ) # TODO: eliminate magic number 100 + # step_id is set by workflow internally (e.g., AsyncCoDMultiStepWorkflow.run_async) + + def _create_sublogs( + self, + task: Task, + exps: List[Experience], + ) -> Dict[str, Union[str, None, dict, list]]: + sublogs: Dict[str, Union[str, None, dict, list]] = {} + # Only include task_desc / truth when they are not None + if task.task_desc is not None: + sublogs["task_desc"] = task.task_desc + if task.truth is not None: + sublogs["truth"] = task.truth + if exps: + last_info = exps[-1].info + if "trajectory" in last_info: + sublogs["trajectory"] = last_info["trajectory"] + if "feedback" in last_info: + sublogs["feedback"] = last_info["feedback"] + + # Deduplicate sys_prompts and hints + sys_prompt_list: List[str] = [] + sys_prompt_map: Dict[str, int] = {} # content -> index + hint_list: List[str] = [] + hint_map: Dict[str, int] = {} # content -> index + + steps: List[dict] = [] + for exp in exps: + taskid = exp.eid.task + runid = exp.eid.run + stepid = exp.eid.step + sys_prompt = exp.info.get("sys_prompt", "(not logged)") + user_prompt = exp.info.get("user_prompt", "(not logged)") + hint = exp.info.get("hint", "(not available)") + response_text = exp.response_text + reward = exp.reward + + # Assign deduplicated index for sys_prompt + if sys_prompt not in sys_prompt_map: + sys_prompt_map[sys_prompt] = len(sys_prompt_list) + sys_prompt_list.append(sys_prompt) + # Assign deduplicated index for hint + if hint not in hint_map: + hint_map[hint] = len(hint_list) + hint_list.append(hint) + + # Simplified step key: strip batch/main/part info (already in dir/file name) + # Original taskid format: batch_X_main_Y_sub_Z[_part_NAME] + # Extract sub_Z portion + sub_part = "" + parts = taskid.split("_") + try: + sub_idx = parts.index("sub") + # Take sub and its value, stop before "part" if present + if "part" in parts: + part_idx = parts.index("part") + sub_part = "_".join(parts[sub_idx:part_idx]) + else: + sub_part = "_".join(parts[sub_idx:]) + except ValueError: + sub_part = taskid # fallback + step_key = f"{sub_part}_run_{runid}_step_{stepid}" + + step_entry = { + "step_key": step_key, + "sys_prompt_idx": sys_prompt_map[sys_prompt], + "hint_idx": hint_map[hint], + "user_prompt": user_prompt, + "response_text": response_text, + "reward": reward, + } + # Additional fields + if "exp_type" in exp.info: + step_entry["exp_type"] = exp.info["exp_type"] + if "incurred_mean_reward" in exp.info: + step_entry["incurred_mean_reward"] = exp.info["incurred_mean_reward"] + if "total_reward" in exp.info: + step_entry["total_reward"] = exp.info["total_reward"] + steps.append(step_entry) + + sublogs["sys_prompts"] = sys_prompt_list + sublogs["hints"] = hint_list + sublogs["steps"] = steps + + # Group steps into episodes by sys_prompt_idx for easier reading + episodes: Dict[int, list] = {} + for step_entry in steps: + ep_id = step_entry["sys_prompt_idx"] + if ep_id not in episodes: + episodes[ep_id] = [] + episodes[ep_id].append(step_entry) + sublogs["episodes"] = episodes + + return sublogs + + # --- CoD methods --- + + async def gen_hint_iteratively( + self, + prev_hint: str, + trajectory: str, + reward: float, + feedback: str, + ) -> Tuple[Experience, str]: + """Run one context-update episode and return (experience, new_hint).""" + self.update_context_workflow.set_context_inputs( + prev_context=prev_hint, + trajectory=trajectory, + reward=reward, + feedback=feedback, + ) + exps = await self.update_context_workflow.run_async() + exp = exps[0] + return exp, exp.info["hint"] + + def _get_or_create_workflow(self, task: Task, idx: int) -> Workflow: + """Get or create workflow instance for a task, updating self.all_workflow_instances. + + Args: + task: The task to create workflow for + idx: Index in self.all_workflow_instances + + Returns: + The workflow instance (either reused or newly created) + """ + if not self.task.workflow_args.get("reuse_workflow_instance", False): + workflow_instance = task.to_workflow( + self._model_for_workflow(task.workflow), + self.auxiliary_model_wrappers, + ) + self.all_workflow_instances[idx] = workflow_instance + return workflow_instance + + workflow_instance = self.all_workflow_instances[idx] + if workflow_instance is None: + workflow_instance = task.to_workflow( + self._model_for_workflow(task.workflow), + self.auxiliary_model_wrappers, + ) + self.all_workflow_instances[idx] = workflow_instance + elif workflow_instance.__class__ == task.workflow and workflow_instance.can_reset: + workflow_instance.reset(task) + else: + workflow_instance = task.to_workflow( + self._model_for_workflow(task.workflow), + self.auxiliary_model_wrappers, + ) + self.all_workflow_instances[idx] = workflow_instance + return workflow_instance + + async def solve_task( + self, + workflow_instance: Workflow, + hint: Optional[str] = None, + icl_examples: Optional[str] = None, + ) -> List[Experience]: + """Solve a task and return all exps (supports multi-step workflows). + + Args: + workflow_instance: Workflow instance to use (must be provided, should be already reset) + hint: Optional hint to guide the model + icl_examples: Optional ICL examples to prepend + + Returns: + List of experiences from solving the task + """ + if hint is not None: + workflow_instance.set_hint(hint=hint) + if icl_examples is not None: + workflow_instance.set_icl_examples(icl_examples) + max_tokens = self.task.workflow_args.get("max_response_tokens_restraint") + if max_tokens: + workflow_instance.set_max_response_tokens_restraint(max_tokens) + exps = await workflow_instance.run_async() + for exp in exps: + exp.info["hint"] = hint if hint else "" + return exps + + async def _run_one_iterative_hint_trajectory_e2e( + self, + part_id: int, + part_name: str, + repeat_id: int, + trajectory_id: str, + ) -> Tuple[List[Experience], float]: + """Run one iterative hint trajectory across all tasks in the pack, for end-to-end meta-training. + + Process: + z0 = "Hints: null" # we might support customizing initial hint later + -> traj T1 for task x1 + -> update z1 + -> traj T2 for task x2 + -> update z2 + -> traj T3 for task x3 + + All samples (including solve_task and gen_hint) will share the same advantage value. + + Assumption: + exp.info should contain fields "trajectory" and "feedback", as input for hint generation. + + Returns: + Tuple of (all_exps, total_reward) + - all_exps: List of all experiences (solve_task exps + gen_hint exps) + - total_reward: Sum of all task rewards (r1 + r2 + r3 + r4) + """ + all_exps: List[Experience] = [] + # gen_hint_exps: List[Experience] = [] + task_rewards: List[float] = [] + current_hint = "Hints: null" + + num_tasks = len(self.all_tasks) + + for task_idx in range(num_tasks): + task = self.all_tasks[task_idx] + taskset_id = get_taskset_id(task) + + workflow_instance = self._get_or_create_workflow(task, task_idx) + + # (1) Solve task + task_exps = await self.solve_task( + workflow_instance=workflow_instance, + hint=current_hint, + ) + + # Post-process solve_task exps + for exp in task_exps: + self._post_process_task_solving_exp( + exp=exp, + subtid=0, # !!! ensure all exps in the trajectory have the same eid.task and eid.run + part_id=part_id, + part_name=part_name, + repeat_id=repeat_id, + ) + exp.info["task_desc"] = task.task_desc + exp.info["trajectory_id"] = trajectory_id + exp.info["task_idx"] = task_idx + exp.info["taskset_id"] = taskset_id + exp.info["exp_type"] = "solve_task" + + # Add position-specific reward and format error metrics (on last exp only) + if task_exps and task_exps[-1].metrics: + task_exps[-1].metrics[f"reward_{part_name}_taskset_{taskset_id}_pos_{task_idx}"] = task_exps[-1].reward + if "format_error_termination" in task_exps[-1].metrics: + task_exps[-1].metrics[f"format_error_{part_name}_taskset_{taskset_id}_pos_{task_idx}"] = task_exps[-1].metrics["format_error_termination"] + + all_exps.extend(task_exps) + + # Get reward and feedback from last exp + task_reward = task_exps[-1].reward if task_exps else 0.0 + task_rewards.append(task_reward) + + # Build trajectory string from all responses + trajectory = self._get_trajectory(task_exps) + + # Get structured feedback from exp.info (set by workflow) + feedback = self._get_feedback(task_exps, task_reward) + + # (2) Generate updated hint (for all tasks except the last one) + if task_idx == num_tasks - 1: + continue + + exp_gen_hint, new_hint = await self.gen_hint_iteratively( + prev_hint=current_hint, + trajectory=trajectory, + reward=task_reward, + feedback=feedback, + ) + + # Post-process gen_hint exp + self._post_process_hint_generation_exp( + exp=exp_gen_hint, + subtid=0, # !!! ensure all exps in the trajectory have the same eid.task and eid.run + part_id=part_id, + part_name=part_name, + repeat_id=repeat_id, + step_id=task_idx, # use task_idx as step_id for hint. TODO: confirm that step_id make no real effect + ) + exp_gen_hint.info["trajectory_id"] = trajectory_id + exp_gen_hint.info["hint_idx"] = task_idx + exp_gen_hint.info["taskset_id"] = taskset_id + exp_gen_hint.info["exp_type"] = "gen_hint" + # gen_hint_exps.append(exp_gen_hint) + all_exps.append(exp_gen_hint) + + current_hint = new_hint + + # Calculate total reward (optionally weighted by hint_reward_decay) + # weight_i = decay^(n-1-i), later tasks weighted more + # e.g., with decay=0.8 and 4 tasks: [0.8^3, 0.8^2, 0.8^1, 0.8^0] + # default decay=1.0 means equal weights (equivalent to plain sum) + task_reward_decay = self.task.workflow_args.get("task_reward_decay", 1.0) + n = len(task_rewards) + total_reward = sum( + r * (task_reward_decay ** (n - 1 - i)) + for i, r in enumerate(task_rewards) + ) + + # Set total reward in exp.info for all exps (for advantage calculation) + for exp in all_exps: + exp.info["total_reward"] = total_reward + exp.info["task_rewards"] = task_rewards + + # Any flagged exp contaminates the shared task_rewards → mask the whole trajectory + # and strip reward-shaped metrics so wandb doesn't log judge-breakage as "wrong answer". + if any(e.info.get("judge_format_error", False) for e in all_exps): + for exp in all_exps: + exp.info["judge_format_error_in_trajectory"] = True + if exp.metrics: + for k in list(exp.metrics.keys()): + if k.startswith("reward") or k.startswith("format_error"): + exp.metrics.pop(k, None) + + return all_exps, total_reward + + async def run_iterative_hint_e2e( + self, + part_id: int, + part_name: str, + subtid: int, + ) -> List[Experience]: + """Run iterative hint for end-to-end meta-training. + + Only runs when subtid == 0 (handles all tasks in one call). + """ + if subtid != 0: + return [] + + all_exps: List[Experience] = [] + + for i in range(self.repeat_times): + trajectory_id = f"batch_{self.task.batch_id}_task_{self.task.task_id}_runidbase_{self.run_id_base}_repeat_{i}" + + exps, total_reward = await self._run_one_iterative_hint_trajectory_e2e( + part_id=part_id, + part_name=part_name, + repeat_id=i, + trajectory_id=trajectory_id, + ) + + all_exps.extend(exps) + + return all_exps + + + def _get_cod_method_mapping(self): + mapping = { + "iterative_hint_e2e": self.run_iterative_hint_e2e, + } + return mapping + + # --- main cod workflow run method --- + + async def run_parts_sequential(self) -> List[Experience]: + """Run diverse CoD methods, record metrics, save logs to json files, return experiences. + + For example, + Part 0: solve directly for each task (baseline method) + Part 1: for each task, first generate hint with aux tasks, then generate solution with hint + Part 2: for each task, first generate hint with aux items, then generate solution with hint + + Things to keep in mind: + + Set task id and step id for each exp carefully, used later by multi-step grpo advantage + + Separate reward metric names that specify rollout methods + + One workflow run -> save logs to one folder, containing one or multiple json files + """ + + cod_method_keys = self.task.workflow_args["activated_cod_methods"] + cod_method_mapping = self._get_cod_method_mapping() + + assert len(cod_method_keys) >= 1 + + returned_exps: List[Experience] = [] + + # --- prepare for logging --- + cod_log_interval = self.task.workflow_args.get("cod_log_interval", 20) + if self.task.is_eval: + explore_step_num = ( + int(str(self.task.batch_id).split("/")[0]) + 1 + ) # (eval) step num starts from 0 + log_prefix = "eval_" + str(self.task.batch_id).split("/")[1] + else: + explore_step_num = int(self.task.batch_id) # (explore) step num starts from 1 + log_prefix = "rollout" + self.save_cod_logs: bool = (explore_step_num - 1) % cod_log_interval == 0 + + save_path = None + if self.save_cod_logs: + log_dir = self.task.workflow_args["log_dir"] + exp_name = self.task.workflow_args["exp_name"] + # batch_id = str(self.task.batch_id) + task_id = str(self.task.task_id) + run_id_base = str(self.run_id_base) + subfolder = f"{log_prefix}_step_{explore_step_num}_task_{task_id}_runidbase_{run_id_base}" + save_path = os.path.join(log_dir, exp_name, subfolder) + # For eval, skip saving if subfolder already exists + if self.task.is_eval and os.path.exists(save_path): + self.save_cod_logs = False + else: + os.makedirs(save_path, exist_ok=True) + + # to be used in later parts that require auxiliary trajectories; aligned with self.all_tasks + self.all_aux_exps: List[Experience] = [] + + # Accumulate per-part summary stats for summary.json + summary_parts: Dict[str, dict] = {} + + # --- one part per activated cod method --- + for part_id, part_name in enumerate(cod_method_keys): + cod_fn = cod_method_mapping[part_name] + exps_part = [] + logs_part = dict() + + async def helper_run_one_task_part(subtid: int): + task = self.all_tasks[subtid] + exps = await cod_fn( + part_id=part_id, + part_name=part_name, + subtid=subtid, + ) + sublogs = self._create_sublogs(task=task, exps=exps) + return exps, sublogs + + # solve multiple tasks in parallel + results_part = await asyncio.gather( + *[helper_run_one_task_part(subtid) for subtid in range(len(self.all_tasks))] + ) + for subtid, rst in enumerate(results_part): + exps, sublogs = rst[0], rst[1] + exps_part.extend(exps) + logs_part[f"subtid_{subtid}"] = sublogs + + returned_exps.extend(exps_part) + if self.save_cod_logs and save_path: + file_name = f"{part_id}_{part_name}.json" + file_path = os.path.join(save_path, file_name) + with open(file_path, "w", encoding="utf-8") as f: + json.dump(logs_part, f, indent=4, ensure_ascii=False) + summary_parts[part_name] = compute_part_summary(exps_part) + + # --- Write summary.json --- + if self.save_cod_logs and save_path and summary_parts: + summary = { + "rollout_step": explore_step_num, + "parts": summary_parts, + } + summary_path = os.path.join(save_path, "summary.json") + with open(summary_path, "w", encoding="utf-8") as f: + json.dump(summary, f, indent=2, ensure_ascii=False) + + return returned_exps + + async def run_parts_parallel(self) -> List[Experience]: + raise NotImplementedError + + async def _maybe_compute_teacher_logprobs(self, exps: List[Experience]) -> None: + """Compute taskset-specific teacher logprobs for CoD training experiences. + + ``auxiliary_model_wrappers`` and training tasksets are matched by their + configuration order. Evaluation is intentionally skipped because its + experiences are not consumed by the trainer. + """ + if ( + not self.task.workflow_args.get("enable_teacher_logprobs", False) + or self.task.is_eval + ): + return + + assert self.auxiliary_model_wrappers is not None + temperature = self.task.rollout_args.temperature + temperature = 1.0 if temperature is None else temperature + + async def score_experience(exp: Experience): + taskset_id = exp.info["taskset_id"] + teacher = self.auxiliary_model_wrappers[taskset_id] + teacher_logprobs = await teacher.logprobs_async( + tokens=exp.tokens.tolist(), + temperature=temperature, + ) + response_start = exp.prompt_length - 1 + teacher_response_logprobs = teacher_logprobs[response_start:] + assert len(teacher_response_logprobs) == len(exp.logprobs), ( + "Teacher/student logprob length mismatch: " + f"teacher={len(teacher_response_logprobs)}, student={len(exp.logprobs)}" + ) + + exp.teacher_logprobs = teacher_response_logprobs + kl_per_token = (exp.logprobs - teacher_response_logprobs) * exp.action_mask + exp.metrics["kl_divergence"] = kl_per_token.sum().item() + + await asyncio.gather(*(score_experience(exp) for exp in exps)) + + async def run_async(self) -> List[Experience]: + """TODO: implement run_parts_parallel if it can bring sufficient gains in efficiency. + + reuse_workflow_instance = self.task.workflow_args["reuse_workflow_instance"] + + if reuse_workflow_instance: + # workflow instance is stateful and resettable, hence need to execute workflow runs sequentially + exps = await self.run_parts_sequential() + else: + # a new workflow instance is created for each workflow run, hence multiple runs can be executed in parallel + exps = await self.run_parts_parallel() + """ + exps = await self.run_parts_sequential() + await self._maybe_compute_teacher_logprobs(exps) + return exps diff --git a/trinity/common/workflows/connect_the_dots/frozen_lake/__init__.py b/trinity/common/workflows/connect_the_dots/frozen_lake/__init__.py new file mode 100644 index 00000000000..07d609659f5 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/frozen_lake/__init__.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +"""CoD (Connect-the-Dots) workflows for FrozenLake environment.""" + +from trinity.common.workflows.connect_the_dots.frozen_lake.workflow import ( + CoDFrozenLakeWorkflow, +) +from trinity.common.workflows.connect_the_dots.frozen_lake.workflow_obscure import ( + CoDFrozenLakeObscureWorkflow, + ALL_PERMUTATIONS, + get_mapping_by_index, + get_random_mapping, +) +from trinity.common.workflows.connect_the_dots.frozen_lake.workflow_obscure_react import ( + CoDFrozenLakeObscureReActWorkflow, +) +from trinity.common.workflows.connect_the_dots.frozen_lake.prompts import ( + load_system_prompt, + load_user_prompt, +) + +__all__ = [ + "CoDFrozenLakeWorkflow", + "CoDFrozenLakeObscureWorkflow", + "CoDFrozenLakeObscureReActWorkflow", + "ALL_PERMUTATIONS", + "get_mapping_by_index", + "get_random_mapping", + "load_system_prompt", + "load_user_prompt", +] diff --git a/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/__init__.py b/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/__init__.py new file mode 100644 index 00000000000..c37eea2daf6 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/__init__.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +"""Prompt management for CoD FrozenLake workflow using Jinja2 templates.""" + +from pathlib import Path +from typing import Optional + +from jinja2 import Environment, FileSystemLoader + +# Get the directory where prompt templates are stored +PROMPTS_DIR = Path(__file__).parent + + +def get_jinja_env() -> Environment: + """Get Jinja2 environment with template loader.""" + return Environment( + loader=FileSystemLoader(PROMPTS_DIR), + trim_blocks=True, + lstrip_blocks=True, + ) + + +def load_system_prompt(**kwargs) -> str: + """Load and render system prompt template. + + Args: + **kwargs: Variables to pass to the template. + + Returns: + Rendered system prompt string. + """ + env = get_jinja_env() + template = env.get_template("system.jinja2") + return template.render(**kwargs) + + +def load_system_prompt_obscure(**kwargs) -> str: + """Load and render obscure system prompt template (numeric actions). + + Args: + **kwargs: Variables to pass to the template. + + Returns: + Rendered system prompt string. + """ + env = get_jinja_env() + template = env.get_template("system_obscure.jinja2") + return template.render(**kwargs) + + +def load_user_prompt( + current_step: int, + max_steps: int, + observation: str, + goal_row: int, + goal_col: int, + is_success: bool = False, + **kwargs, +) -> str: + """Load and render user prompt template. + + Args: + current_step: Current step number (1-indexed for display). + max_steps: Maximum number of steps. + observation: Current map observation. + is_success: Whether player has reached the goal. + **kwargs: Additional variables to pass to the template. + + Returns: + Rendered user prompt string. + """ + env = get_jinja_env() + template = env.get_template("user.jinja2") + return template.render( + current_step=current_step, + max_steps=max_steps, + observation=observation, + goal_row=goal_row, + goal_col=goal_col, + is_success=is_success, + **kwargs, + ) + + +__all__ = ["load_system_prompt", "load_system_prompt_obscure", "load_user_prompt", "PROMPTS_DIR"] diff --git a/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/system.jinja2 b/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/system.jinja2 new file mode 100644 index 00000000000..c97f2518067 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/system.jinja2 @@ -0,0 +1,28 @@ +You are playing a FrozenLake game. Your goal is to reach the goal (G) from your current position (P). + +## Symbols +- P: Player (your current position) +- G: Goal (destination) +- _: Frozen tile (safe to walk) +- O: Hole (fall in and lose) + +## Rules +1. Avoid falling into holes (O). +2. Reach the goal (G) to win. +3. The map is a grid with boundaries at its edges. If you try to move beyond the edge, your position will not change and the map will remain the same. + +## Valid Actions +Up | Down | Left | Right + +## Rewards +- Fall into hole: 0 +- Reach goal: +1.0 + +## Response Format +First, wrap your reasoning in ... tags, then provide your final action using ... tags. + +The action must be exactly one of: Up, Down, Left, Right. + +Example: +The goal is to my right, so I should move right. +Right \ No newline at end of file diff --git a/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/system_obscure.jinja2 b/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/system_obscure.jinja2 new file mode 100644 index 00000000000..980cd92e11e --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/system_obscure.jinja2 @@ -0,0 +1,36 @@ +You are playing a FrozenLake-Obscure game, where the action-to-direction mapping is unknown. Your task is to reach the goal (G) from your current position (P). + +## Symbols +- P: Player (your current position) +- G: Goal (destination) +- _: Frozen tile (safe to walk) +- O: Hole (fall in and lose) + +## Rules +1. Avoid falling into holes (O). +2. Reach the goal (G) to win. +3. The map is a grid with boundaries at its edges. If you try to move beyond the edge, your position will not change and the map will remain the same. + +## Valid Actions +You can execute one of the following actions at each step: Direction 1, Direction 2, Direction 3, Direction 4. + +## Hidden Clues About the Environment +Each valid action corresponds to moving along one of the up/down/left/right directions, but the exact action-to-direction mapping is unknown. Do not make any prior assumption about the mapping. You may try to figure it out through trial-and-error, e.g., by observing how your position (P) changes on the map after executing an action. You may also utilize additional hints provided in the context. + +## Rewards +- Fall into hole (O): 0 +- Reach goal (G): 1 + +## Response Format +First think about which action to take at the current step, then provide your final answer. Format your complete response as follows: +``` +[THINKING] +Direction X +``` +where [THINKING] should be replaced with your thinking process, and X is one of 1/2/3/4. + +Other requirements: +- Be concise and avoid overthinking in your thinking process. +- Take one and only one action for each step. +- You should reach the goal (G) within a limited number of steps. Try to find a short path towards the goal. +- Balance exploration and exploitation: as you strive to reach the goal, you may acquire information about the environment along the way, which can be potentially helpful for solving other similar tasks later on. diff --git a/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/user.jinja2 b/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/user.jinja2 new file mode 100644 index 00000000000..d562a3e7fd1 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/frozen_lake/prompts/user.jinja2 @@ -0,0 +1,11 @@ +Step {{ current_step }}/{{ max_steps }} +{% if action_feedback %} + +{{ action_feedback }} +{% endif %} + +Current map: +{{ observation }} +{% if not is_success %} +You have not reached the goal (row={{ goal_row }}, col={{ goal_col }}) yet. Please decide your next action. +{% endif %} diff --git a/trinity/common/workflows/connect_the_dots/frozen_lake/workflow.py b/trinity/common/workflows/connect_the_dots/frozen_lake/workflow.py new file mode 100644 index 00000000000..d4571b9a918 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/frozen_lake/workflow.py @@ -0,0 +1,371 @@ +# -*- coding: utf-8 -*- +""" +CoD (Connect-the-Dots) workflow for FrozenLake environment using generic multi-turn. + +Each step produces an independent Experience with step number. Prior assistant +responses are compressed to tags only by the base class to avoid +context bloat in multi-turn conversations. +""" + +from __future__ import annotations + +import copy +from typing import TYPE_CHECKING, List, Optional, Tuple + +import numpy as np + +from trinity.common.experience import Experience +from trinity.common.workflows.connect_the_dots.base_workflow import AsyncCoDMultiStepWorkflow +from trinity.common.workflows.connect_the_dots.utils import extract_content_between_keys +from trinity.common.workflows.workflow import Task +from trinity.common.workflows.envs.frozen_lake.utils import ( + GRID_LOOKUP, + MAP_LOOKUP, + generate_random_map, + get_goal_position, +) +from trinity.common.workflows.connect_the_dots.frozen_lake.prompts import ( + load_system_prompt, + load_user_prompt, +) + +if TYPE_CHECKING: + from trinity.common.models.model import ModelWrapper + + +def parse_action(response: str) -> Optional[str]: + """Parse action from model response. + + Args: + response: Model response text. + + Returns: + Action string if found, None otherwise. + """ + VALID_ACTIONS = {"up", "down", "left", "right"} + + key_start, key_end = "", "" + content, success = extract_content_between_keys(response, key_start, key_end) + if not success: + return None + + action = content.strip().lower() + if action in VALID_ACTIONS: + return action.capitalize() + return None + + +class CoDFrozenLakeWorkflow(AsyncCoDMultiStepWorkflow): + """ + CoD FrozenLake workflow using generic multi-turn mechanism. + + This workflow: + - Produces independent Experience per step (with exp.eid.step) + - Prior assistant responses are compressed to only (via base class) + """ + + is_async: bool = True + + def __init__( + self, + model: ModelWrapper, + task: Task, + auxiliary_models: Optional[List] = None, + use_openai_client: bool = False, + ): + super().__init__( + model=model, + task=task, + auxiliary_models=auxiliary_models, + use_openai_client=use_openai_client, + ) + + # Import gymnasium here to avoid import error if not installed + try: + import gymnasium as gym + from gymnasium.envs.toy_text.frozen_lake import FrozenLakeEnv as GymFrozenLakeEnv + except ImportError as e: + raise ImportError( + f"Gymnasium is not installed. Please install gymnasium first. Error: {e}" + ) + + self._gym = gym + self._GymFrozenLakeEnv = GymFrozenLakeEnv + + # Action mapping: our action -> gym action + self.action_map = { + "Left": 0, + "Down": 1, + "Right": 2, + "Up": 3, + } + + self.reset(task) + + def reset(self, task: Task): + """Reset task-specific config, environment, and episode state.""" + super().reset(task) + + # Extract workflow-specific arguments + workflow_args = task.workflow_args if hasattr(task, "workflow_args") else {} + self.env_max_steps = workflow_args.get("env_max_steps", 8) + self.agent_max_steps = workflow_args.get("agent_max_steps", 10) + self.desc = workflow_args.get("desc", None) + self.is_slippery = workflow_args.get("is_slippery", False) + self.max_response_tokens_restraint = workflow_args.get("max_response_tokens_restraint", None) + + # Extract task-specific arguments + self.raw_task = task.raw_task if hasattr(task, "raw_task") else {} + if self.raw_task is None: + self.raw_task = {} + self.size = self.raw_task.get("size", 4) + self.p = self.raw_task.get("p", 0.8) + self.seed = self.raw_task.get("seed", 42) + + # Generate or use provided map + if self.desc is None: + random_map, goal_position = generate_random_map( + size=self.size, p=self.p, seed=self.seed, max_steps=self.env_max_steps + ) + else: + random_map = np.asarray(copy.deepcopy(self.desc), dtype="c") + goal_position = get_goal_position(random_map) + + self.goal_position = goal_position + + # Create gym environment + self.gym_env = self._GymFrozenLakeEnv( + desc=random_map[:], + is_slippery=self.is_slippery, + ) + self.action_space = self._gym.spaces.Discrete(4, start=1) + + # State variables + self.observation: Optional[str] = None + self.done: bool = False + self.final_reward: float = 0.0 + self.early_termination_by_format_issue = False + self.memory: List[dict] = [] + self.current_step: int = 0 + self.action_feedback = None + + def _get_player_position(self): + """Get current player position as (row, col).""" + return ( + self.gym_env.s // self.gym_env.ncol, + self.gym_env.s % self.gym_env.ncol, + ) + + def _is_success(self) -> bool: + """Check if player reached the goal.""" + player_pos = self._get_player_position() + return self.gym_env.desc[player_pos] == b"G" + + def render(self) -> str: + """Render current state as text.""" + room_state = copy.deepcopy(self.gym_env.desc) + + # Replace start 'S' with frozen 'F' + position_S = np.where(room_state == b"S") + room_state[position_S] = b"F" + + # Mark player position + position_P = self._get_player_position() + room_state[position_P] = b"P" + + # Convert to state array + state_array = np.vectorize(lambda x: MAP_LOOKUP[x])(room_state) + + # Handle player on hole or goal + if self.gym_env.desc[position_P] == b"H": + state_array[position_P] = 4 # player fell into hole + elif self.gym_env.desc[position_P] == b"G": + state_array[position_P] = 5 # player on goal + + # Render as text + result = "\n".join( + "".join(GRID_LOOKUP.get(cell, "?") for cell in row) + for row in state_array.tolist() + ) + return result + + def _build_action_feedback( + self, action_str: Optional[str], prev_pos: Tuple[int, int], + cur_pos: Tuple[int, int], action_effective: bool, + ) -> str: + """Build position change feedback string after an action. + + Args: + action_str: The action taken (e.g. "Up" or None if invalid). + prev_pos: (row, col) before action. + cur_pos: (row, col) after action. + action_effective: Whether the position changed. + + Returns: + Feedback string describing position change. + """ + if action_str is None: + return "Your action was invalid. Position unchanged." + if action_effective: + return ( + f"You executed action {action_str}. " + f"Your position changed from (row={prev_pos[0]}, col={prev_pos[1]}) " + f"to (row={cur_pos[0]}, col={cur_pos[1]})." + ) + else: + return ( + f"You executed action {action_str}. " + f"Your position did not change (stayed at row={cur_pos[0]}, col={cur_pos[1]}). " + f"This may be because you tried to move beyond the edge of the map." + ) + + def env_step(self, action: Optional[str]): + """Execute action in environment. + + Args: + action: Action string (Up/Down/Left/Right) or None for invalid. + + Returns: + Tuple of (observation, reward, done, info). + """ + if self._is_success(): + return self.render(), 1.0, True, {"action_is_effective": False} + + if action is None or action not in self.action_map: + return self.render(), 0.0, False, {"action_is_effective": False} + + prev_pos = int(self.gym_env.s) + + # Execute action + _, reward, done, _, _ = self.gym_env.step(self.action_map[action]) + + obs = self.render() + action_effective = prev_pos != int(self.gym_env.s) + + return obs, reward, done, {"action_is_effective": action_effective} + + def _build_system_prompt(self) -> str: + """Build system prompt with hint and token limit.""" + sys_prompt = load_system_prompt() + sys_prompt = self._augment_system_prompt(sys_prompt) + return sys_prompt + + async def run_async(self) -> List[Experience]: + """Run the workflow.""" + # Reset environment + self.gym_env.reset(seed=self.seed) + self.observation = self.render() + self.done = False + self.final_reward = 0.0 + self.current_step = 0 + self.action_feedback = None # Position change feedback for next step + + # Initialize memory with system prompt (includes hint and token limit) + self.memory.clear() + self.memory.append({"role": "system", "content": self._build_system_prompt()}) + + return await super().run_async() + + async def step_async(self, step_num: int) -> Tuple[bool, List[Experience]]: + """Execute one step of the workflow. + + Args: + step_num: Current step number. + + Returns: + Tuple of (continue_run, experiences): + - continue_run: True to continue, False to stop. + - experiences: List of experiences from this step. + """ + if self.done: + return False, [] + + # Format observation as user message using jinja template + user_content = load_user_prompt( + current_step=step_num + 1, + max_steps=self.agent_max_steps, + observation=self.observation, + goal_row=self.goal_position[0], + goal_col=self.goal_position[1], + is_success=self._is_success(), + action_feedback=self.action_feedback, + ) + + # ========== CoD-specific: add icl_examples and reply_prefix ========== + # icl_examples: only added in the first turn to avoid token waste + if self.icl_examples and step_num == 0: + user_content = f"{user_content}\n\nHere are some reference examples:\n\n{self.icl_examples}" + + self.memory.append({"role": "user", "content": user_content}) + + # reply_prefix: prefix for model's reply, used for guided generation + if self.reply_prefix: + self.memory.append({"role": "assistant", "content": self.reply_prefix}) + # ==================================================================== + + # Get model response - chat_async returns List[Experience] directly + experiences = await self.model.chat_async(self.memory) + response_text = experiences[0].response_text + self.memory.append({"role": "assistant", "content": response_text}) + + # Store prompt information in exp.info for logging + sys_prompt = self.memory[0]["content"] if self.memory and self.memory[0]["role"] == "system" else "" + for exp in experiences: + exp.info["sys_prompt"] = sys_prompt + exp.info["user_prompt"] = user_content + + # Parse action + action = parse_action(response_text) + + # Track position before action + prev_pos = self._get_player_position() + + # Execute action + observation, reward, done, info = self.env_step(action) + + # Build action feedback for next step + cur_pos = self._get_player_position() + self.action_feedback = self._build_action_feedback( + action_str=action, prev_pos=prev_pos, cur_pos=cur_pos, + action_effective=info.get("action_is_effective", False), + ) + + # Update state + self.observation = observation + self.done = done + self.current_step = step_num + 1 + + if done and reward > 0: + self.final_reward = reward + + return not self.done, experiences + + def _get_feedback(self) -> str: + """Generate structured feedback about task completion. + + Returns feedback relevant to reward calculation: + - Success: reached the goal + - Failure: fell into hole or ran out of steps + """ + if self._is_success(): + return "Success! Reached the goal (G)." + + player_pos = self._get_player_position() + if self.gym_env.desc[player_pos] == b"H": + return "Failed: Fell into a hole (O). Avoid holes by planning a safe path." + + if self.early_termination_by_format_issue: + return self.action_feedback + + # Ran out of steps + return f"Failed: Ran out of steps ({self.current_step}/{self.agent_max_steps}) without reaching the goal. Try to find a shorter path." + + @property + def max_step_num(self) -> int: + """Maximum number of steps.""" + return self.agent_max_steps + + def __del__(self): + """Cleanup environment.""" + if hasattr(self, "gym_env"): + self.gym_env.close() diff --git a/trinity/common/workflows/connect_the_dots/frozen_lake/workflow_obscure.py b/trinity/common/workflows/connect_the_dots/frozen_lake/workflow_obscure.py new file mode 100644 index 00000000000..055f7598d81 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/frozen_lake/workflow_obscure.py @@ -0,0 +1,222 @@ +# -*- coding: utf-8 -*- +""" +CoD FrozenLake Obscure Workflow - uses numeric actions (1,2,3,4) instead of directions. + +Mapping modes: +- "global": All episodes use the same fixed mapping +- "per_pack": Tasks in the same CoD pack share one mapping +- "per_task": Each task uses its own mapping (via raw_task) +""" + +from __future__ import annotations + +import itertools +import random +import re +from typing import Dict, List, Optional, Tuple, TYPE_CHECKING + +from trinity.common.experience import Experience +from trinity.common.workflows.connect_the_dots.frozen_lake.workflow import ( + CoDFrozenLakeWorkflow, +) +from trinity.common.workflows.connect_the_dots.utils import extract_content_between_keys + +if TYPE_CHECKING: + from trinity.common.models.model import ModelWrapper + from trinity.common.workflows.workflow import Task + + +# All 24 permutations of directions +ALL_PERMUTATIONS = list(itertools.permutations(["Down", "Right", "Up", "Left"])) + +# Default mapping: 1=Down, 2=Right, 3=Up, 4=Left +DEFAULT_ACTION_MAPPING = {1: "Down", 2: "Right", 3: "Up", 4: "Left"} + + +def get_mapping_by_index(index: int) -> Dict[int, str]: + """Get action mapping by permutation index (0-23).""" + perm = ALL_PERMUTATIONS[index % len(ALL_PERMUTATIONS)] + return {i + 1: perm[i] for i in range(4)} + + +def get_random_mapping(seed: Optional[int] = None) -> Dict[int, str]: + """Get a random action mapping.""" + rng = random.Random(seed) + directions = ["Down", "Right", "Up", "Left"] + rng.shuffle(directions) + return {i + 1: directions[i] for i in range(4)} + + +def parse_numeric_action_number(response: str) -> Optional[int]: + """Parse 'Direction X' action from response, return the integer or None.""" + key_start, key_end = "", "" + content, success = extract_content_between_keys(response, key_start, key_end) + if not success: + return None + action_str = content.strip() + dir_match = re.match(r"[Dd]irection\s+(\d+)", action_str) + if dir_match: + return int(dir_match.group(1)) + return None + + +class CoDFrozenLakeObscureWorkflow(CoDFrozenLakeWorkflow): + """ + FrozenLake with numeric actions (1,2,3,4) instead of Up/Down/Left/Right. + + Configuration (workflow_args): + - mapping_mode: "global", "per_pack", or "per_task" + - "global": All tasks use the same fixed mapping + - "per_pack": All tasks in one CoD pack share one mapping, + different packs get different mappings + - "per_task": Each task uses its own mapping + - global_mapping: {1: "Down", ...} for global mode + - mapping_index: 0-23 for predefined permutations (global mode) + + Per-task (raw_task, for per_task mode): + - action_mapping: explicit mapping + - mapping_index: permutation index + - mapping_seed: seed for random mapping + """ + + def __init__( + self, + model: ModelWrapper, + task: Task, + auxiliary_models: Optional[List] = None, + use_openai_client: bool = False, + ): + super().__init__( + model=model, + task=task, + auxiliary_models=auxiliary_models, + use_openai_client=use_openai_client, + ) + + def reset(self, task: Task): + """Reset FrozenLake state and recompute numeric action mapping.""" + workflow_args = task.workflow_args if hasattr(task, "workflow_args") else {} + self.mapping_mode = workflow_args.get("mapping_mode", "global") + self.global_mapping = workflow_args.get("global_mapping", None) + self.global_mapping_index = workflow_args.get("mapping_index", None) + + super().reset(task) + self.action_mapping = self._determine_action_mapping() + + def _determine_action_mapping(self) -> Dict[int, str]: + """Determine action mapping based on mode. + + Modes: + - "global": All tasks use the same mapping (from workflow_args) + - "per_pack": All tasks in the same CoD pack share one mapping, + different packs use different mappings + - "per_task": Each task uses its own mapping (from raw_task) + """ + if self.mapping_mode == "per_pack": + # Use pack-level seed injected by CoDWorkflow.reset() + if "pack_seed" in self.raw_task: + return get_random_mapping(self.raw_task["pack_seed"]) + else: + # Fallback: not running under CoDWorkflow, use task seed + return get_random_mapping(self.raw_task.get("seed", 42)) + elif self.mapping_mode == "per_task": + if "action_mapping" in self.raw_task: + return self.raw_task["action_mapping"] + elif "mapping_index" in self.raw_task: + return get_mapping_by_index(self.raw_task["mapping_index"]) + elif "mapping_seed" in self.raw_task: + return get_random_mapping(self.raw_task["mapping_seed"]) + else: + return get_random_mapping(self.raw_task.get("seed", 42)) + else: + # "global" mode + if self.global_mapping is not None: + return self.global_mapping + elif self.global_mapping_index is not None: + return get_mapping_by_index(self.global_mapping_index) + else: + return DEFAULT_ACTION_MAPPING + + def _build_system_prompt(self) -> str: + """Build system prompt with numeric actions using jinja template.""" + from trinity.common.workflows.connect_the_dots.frozen_lake.prompts import load_system_prompt_obscure + + sys_prompt = load_system_prompt_obscure() + sys_prompt = self._augment_system_prompt(sys_prompt) + return sys_prompt + + async def step_async(self, step_num: int) -> Tuple[bool, List[Experience]]: + """Execute one step - override to use numeric action parsing.""" + if self.done: + return False, [] + + from trinity.common.workflows.connect_the_dots.frozen_lake.prompts import load_user_prompt + + user_content = load_user_prompt( + current_step=step_num + 1, + max_steps=self.agent_max_steps, + observation=self.observation, + goal_row=self.goal_position[0], + goal_col=self.goal_position[1], + is_success=self._is_success(), + action_feedback=self.action_feedback, + ) + + if self.icl_examples and step_num == 0: + user_content = f"{user_content}\n\nHere are some reference examples:\n\n{self.icl_examples}" + + self.memory.append({"role": "user", "content": user_content}) + + if self.reply_prefix: + self.memory.append({"role": "assistant", "content": self.reply_prefix}) + + experiences = await self.model.chat_async(self.memory) + response_text = experiences[0].response_text + self.memory.append({"role": "assistant", "content": response_text}) + + sys_prompt = self.memory[0]["content"] if self.memory and self.memory[0]["role"] == "system" else "" + for exp in experiences: + exp.info["sys_prompt"] = sys_prompt + exp.info["user_prompt"] = user_content + exp.info["action_mapping"] = self.action_mapping + + # Check for malformed response: missing tag + numeric_action = parse_numeric_action_number(response_text) + if numeric_action is None: + # Early termination: reward 0, give feedback about format + self.action_feedback = "Invalid format: could not parse action. Expected format: {your reasoning process here}Direction X, where X is 1, 2, 3, or 4. Game over." + self.done = True + self.current_step = step_num + 1 + self.early_termination_by_format_issue = True + return False, experiences + + direction = self.action_mapping.get(numeric_action) + if direction is None: + # Valid format but invalid action number (e.g. Direction 5) + self.action_feedback = f"Invalid format: Direction {numeric_action} is not a valid action. Expected: Direction 1, 2, 3, or 4. Game over." + self.done = True + self.current_step = step_num + 1 + self.early_termination_by_format_issue = True + return False, experiences + + # Track position before action + prev_pos = self._get_player_position() + + observation, reward, done, info = self.env_step(direction) + + # Build action feedback with "Direction X" format + cur_pos = self._get_player_position() + self.action_feedback = self._build_action_feedback( + action_str=f"Direction {numeric_action}", + prev_pos=prev_pos, cur_pos=cur_pos, + action_effective=info.get("action_is_effective", False), + ) + + self.observation = observation + self.done = done + self.current_step = step_num + 1 + + if done and reward > 0: + self.final_reward = reward + + return not self.done, experiences diff --git a/trinity/common/workflows/connect_the_dots/frozen_lake/workflow_obscure_react.py b/trinity/common/workflows/connect_the_dots/frozen_lake/workflow_obscure_react.py new file mode 100644 index 00000000000..a31c1ad8110 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/frozen_lake/workflow_obscure_react.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +"""ReAct-agent version of the CoD FrozenLake-Obscure solve workflow. + +An AgentScope ReActAgent emits one numeric action per environment turn; the +workflow steps the environment and feeds the next observation back. Each model +call inside a turn is captured via an isolated-history model clone. +""" + +from __future__ import annotations + +from typing import List, Tuple + +from trinity.common.experience import Experience +from trinity.common.workflows.connect_the_dots.agentscope_utils import ( + build_agentscope_react_agent, + run_agentscope_agent_step, +) +from trinity.common.workflows.connect_the_dots.frozen_lake.prompts import load_user_prompt +from trinity.common.workflows.connect_the_dots.frozen_lake.workflow_obscure import ( + CoDFrozenLakeObscureWorkflow, + parse_numeric_action_number, +) + + +class CoDFrozenLakeObscureReActWorkflow(CoDFrozenLakeObscureWorkflow): + """FrozenLake-Obscure solved by a ReActAgent that acts once per turn. + + Env, mapping, prompts, feedback and reward are inherited from the parent; + this class replaces only the generation path. + """ + + is_async: bool = True + can_reset: bool = True + requires_isolated_model_history: bool = True + + async def run_async(self) -> List[Experience]: + # Reset env and per-episode state. + self.gym_env.reset(seed=self.seed) + self.observation = self.render() + self.done = False + self.final_reward = 0.0 + self.current_step = 0 + self.action_feedback = None + self.early_termination_by_format_issue = False + + # reply_prefix cannot be injected through the agent loop; fail loudly + # rather than diverge silently. + if self.reply_prefix: + raise NotImplementedError( + "reply_prefix is not supported by the ReAct solve workflow." + ) + + # Rebuild the agent each run since the hint in the system prompt changes. + self.model.history.clear() + sys_prompt = self._build_system_prompt() + agent = await build_agentscope_react_agent( + name="cod_frozenlake_obscure", + model=self.model, + system_prompt=sys_prompt, + compress_assistant_fn=self._compress_assistant_response, + max_iters=self.task.workflow_args.get("react_max_iters", 1), + ) + + history_spans: List[Tuple[int, int, str]] = [] + for step_num in range(self.agent_max_steps): + if self.done: + break + user_content = load_user_prompt( + current_step=step_num + 1, + max_steps=self.agent_max_steps, + observation=self.observation, + goal_row=self.goal_position[0], + goal_col=self.goal_position[1], + is_success=self._is_success(), + action_feedback=self.action_feedback, + ) + if self.icl_examples and step_num == 0: + user_content = ( + f"{user_content}\n\nHere are some reference examples:\n\n" + f"{self.icl_examples}" + ) + + history_start = len(self.model.history) + response_text = await run_agentscope_agent_step(agent, user_content) + history_spans.append( + (history_start, len(self.model.history), user_content) + ) + + numeric_action = parse_numeric_action_number(response_text) + if numeric_action is None: + self.action_feedback = ( + "Invalid format: could not parse action. Expected format: " + "{your reasoning process here}Direction X, " + "where X is 1, 2, 3, or 4. Game over." + ) + self.done = True + self.early_termination_by_format_issue = True + self.current_step = step_num + 1 + break + + direction = self.action_mapping.get(numeric_action) + if direction is None: + self.action_feedback = ( + f"Invalid format: Direction {numeric_action} is not a valid " + "action. Expected: Direction 1, 2, 3, or 4. Game over." + ) + self.done = True + self.early_termination_by_format_issue = True + self.current_step = step_num + 1 + break + + prev_pos = self._get_player_position() + observation, reward, done, info = self.env_step(direction) + cur_pos = self._get_player_position() + self.action_feedback = self._build_action_feedback( + action_str=f"Direction {numeric_action}", + prev_pos=prev_pos, + cur_pos=cur_pos, + action_effective=info.get("action_is_effective", False), + ) + self.observation = observation + self.done = done + self.current_step = step_num + 1 + if done and reward > 0: + self.final_reward = reward + + experiences = self.model.extract_experience_from_history() + exp_step = 0 + for history_start, history_end, user_content in history_spans: + for exp in experiences[history_start:history_end]: + exp.eid.step = exp_step + exp.info["sys_prompt"] = sys_prompt + exp.info["user_prompt"] = user_content + exp.info["action_mapping"] = self.action_mapping + exp_step += 1 + + trajectory = self._build_agentscope_trajectory( + sys_prompt, + await agent.memory.get_memory(prepend_summary=False), + ) + + reward = await self.reward_async(experiences) + for exp in experiences: + exp.reward = reward + if exp.metrics is None: + exp.metrics = {} + if experiences: + experiences[-1].metrics["actual_env_steps"] = self.current_step + experiences[-1].info["trajectory"] = trajectory + return experiences diff --git a/trinity/common/workflows/connect_the_dots/grid_navigation/__init__.py b/trinity/common/workflows/connect_the_dots/grid_navigation/__init__.py new file mode 100644 index 00000000000..108fb8a00d5 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/grid_navigation/__init__.py @@ -0,0 +1,17 @@ +"""CoD grid-navigation environment and workflow.""" + +from trinity.common.workflows.connect_the_dots.grid_navigation.env import ( + GridNavigationEnv, + GridNavigationPackState, + GridNavigationTask, +) +from trinity.common.workflows.connect_the_dots.grid_navigation.workflow import ( + CoDGridNavigationWorkflow, +) + +__all__ = [ + "CoDGridNavigationWorkflow", + "GridNavigationEnv", + "GridNavigationPackState", + "GridNavigationTask", +] diff --git a/trinity/common/workflows/connect_the_dots/grid_navigation/env.py b/trinity/common/workflows/connect_the_dots/grid_navigation/env.py new file mode 100644 index 00000000000..a9444e87a0c --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/grid_navigation/env.py @@ -0,0 +1,267 @@ +"""Grid-navigation environment used by the CoD research workflow.""" + +from __future__ import annotations + +import weakref +from dataclasses import dataclass +from typing import Optional, Tuple + +import numpy as np + +Position = Tuple[int, int] + + +def _generate_landscape_costs( + *, + rng: np.random.Generator, + size: int, + num_components: int, + min_scale: float, + max_scale: float, +) -> np.ndarray: + """Generate a spatially correlated integer cost landscape.""" + if num_components < 2: + raise ValueError("landscape_num_components must be at least 2.") + if not 0.0 < min_scale <= max_scale: + raise ValueError("landscape scales must satisfy 0 < min_scale <= max_scale.") + + coordinates = np.linspace(0.0, 1.0, size) + row_grid, col_grid = np.meshgrid(coordinates, coordinates, indexing="ij") + landscape = np.zeros((size, size), dtype=np.float64) + + for _ in range(num_components): + center_row, center_col = rng.uniform(0.0, 1.0, size=2) + long_scale = float(rng.uniform(min_scale, max_scale)) + short_scale = float(rng.uniform(min_scale, long_scale)) + angle = float(rng.uniform(0.0, np.pi)) + amplitude = float(rng.uniform(0.8, 1.2)) + + row_delta = row_grid - center_row + col_delta = col_grid - center_col + cos_angle = np.cos(angle) + sin_angle = np.sin(angle) + long_delta = cos_angle * row_delta + sin_angle * col_delta + short_delta = -sin_angle * row_delta + cos_angle * col_delta + landscape += amplitude * np.exp( + -0.5 * ((long_delta / long_scale) ** 2 + (short_delta / short_scale) ** 2) + ) + + # Positive Gaussian components form costly hills over a connected low-cost + # background. Squaring the normalized height makes routes through valleys cheap + # without flattening the spatial differences through a rank transformation. + landscape_min = float(landscape.min()) + landscape_span = float(landscape.max() - landscape_min) + if landscape_span == 0.0: + return np.zeros_like(landscape, dtype=np.int64) + normalized = (landscape - landscape_min) / landscape_span + costs = np.rint(99.0 * np.square(normalized)) + return costs.astype(np.int64) + + +@dataclass +class GridNavigationPackState: + """The cost map and observations shared by all tasks in one CoD pack.""" + + pack_seed: int + costs: np.ndarray + revealed: np.ndarray + + @classmethod + def generate( + cls, + pack_seed: int, + grid_min_size: int, + grid_max_size: int, + landscape_num_components: int = 6, + landscape_min_scale: float = 0.10, + landscape_max_scale: float = 0.35, + ) -> "GridNavigationPackState": + """Generate a deterministic square cost map for one pack.""" + rng = np.random.default_rng(pack_seed) + size = int(rng.integers(grid_min_size, grid_max_size + 1)) + costs = _generate_landscape_costs( + rng=rng, + size=size, + num_components=landscape_num_components, + min_scale=landscape_min_scale, + max_scale=landscape_max_scale, + ) + return cls( + pack_seed=pack_seed, + costs=costs, + revealed=np.zeros_like(costs, dtype=bool), + ) + + @property + def size(self) -> int: + """Return the side length of the square grid.""" + return int(self.costs.shape[0]) + + def reveal(self, path: list[Position], radius: int) -> None: + """Reveal the Chebyshev neighborhood of every entered cell.""" + for row, col in path: + row_start = max(0, row - radius) + row_end = min(self.size, row + radius + 1) + col_start = max(0, col - radius) + col_end = min(self.size, col + radius + 1) + self.revealed[row_start:row_end, col_start:col_end] = True + + +_PACK_STATES: weakref.WeakValueDictionary = weakref.WeakValueDictionary() + + +def get_pack_state( + *, + pack_seed: int, + task_idx: int, + grid_min_size: int, + grid_max_size: int, + landscape_num_components: int = 6, + landscape_min_scale: float = 0.10, + landscape_max_scale: float = 0.35, +) -> GridNavigationPackState: + """Create the state for task 0, and reuse it for later tasks in the pack.""" + key = pack_seed + if task_idx == 0: + state = GridNavigationPackState.generate( + pack_seed=pack_seed, + grid_min_size=grid_min_size, + grid_max_size=grid_max_size, + landscape_num_components=landscape_num_components, + landscape_min_scale=landscape_min_scale, + landscape_max_scale=landscape_max_scale, + ) + _PACK_STATES[key] = state + return state + return _PACK_STATES[key] + + +@dataclass(frozen=True) +class GridNavigationTask: + """Task-specific start, goal, and exact round count.""" + + start: Position + goal: Position + num_rounds: int + + +def _sample_other_coordinate(rng: np.random.Generator, current: int, size: int) -> int: + value = int(rng.integers(0, size - 1)) + return value + 1 if value >= current else value + + +def generate_task( + *, + task_seed: int, + grid_size: int, + min_rounds: int, + max_rounds: int, +) -> GridNavigationTask: + """Generate a task together with an implicit exact-length witness path.""" + rng = np.random.default_rng(task_seed) + num_rounds = int(rng.integers(min_rounds, max_rounds + 1)) + + while True: + start = ( + int(rng.integers(0, grid_size)), + int(rng.integers(0, grid_size)), + ) + current = start + for _ in range(num_rounds): + row, col = current + if int(rng.integers(0, 2)) == 0: + current = (row, _sample_other_coordinate(rng, col, grid_size)) + else: + current = (_sample_other_coordinate(rng, row, grid_size), col) + if current != start: + return GridNavigationTask(start=start, goal=current, num_rounds=num_rounds) + + +class GridNavigationEnv: + """One fixed-round navigation task over a shared cost map.""" + + def __init__( + self, + *, + pack_state: GridNavigationPackState, + task: GridNavigationTask, + reveal_radius: int, + ): + """Initialize one task over an existing pack state.""" + self.pack_state = pack_state + self.task = task + self.reveal_radius = reveal_radius + self.reset_task() + + def reset_task(self) -> None: + """Reset task-local state while retaining pack observations.""" + self.current_position = self.task.start + self.rounds_taken = 0 + self.entered_costs: list[int] = [] + self.last_path: list[Position] = [] + + @property + def normalized_loss(self) -> float: + """Return the visit-weighted mean cell cost normalized to [0, 1].""" + if not self.entered_costs: + return 0.0 + return float(sum(self.entered_costs) / (100.0 * len(self.entered_costs))) + + def validate_destination(self, destination: Position) -> Optional[str]: + """Return an error for an illegal rook move, otherwise None.""" + row, col = destination + cur_row, cur_col = self.current_position + if not (0 <= row < self.pack_state.size and 0 <= col < self.pack_state.size): + return f"Destination ({row},{col}) is outside the grid." + if destination == self.current_position: + return "The destination must differ from the current position." + if row != cur_row and col != cur_col: + return "The destination must be in the same row or the same column." + return None + + def path_to(self, destination: Position) -> list[Position]: + """List entered cells, excluding the origin and including the destination.""" + cur_row, cur_col = self.current_position + dest_row, dest_col = destination + if cur_row == dest_row: + step = 1 if dest_col > cur_col else -1 + return [(cur_row, col) for col in range(cur_col + step, dest_col + step, step)] + step = 1 if dest_row > cur_row else -1 + return [(row, cur_col) for row in range(cur_row + step, dest_row + step, step)] + + def step(self, destination: Position) -> dict: + """Execute one previously validated move.""" + path = self.path_to(destination) + step_costs = [int(self.pack_state.costs[position]) for position in path] + self.entered_costs.extend(step_costs) + self.pack_state.reveal(path, self.reveal_radius) + self.current_position = destination + self.rounds_taken += 1 + self.last_path = path + + done = self.rounds_taken == self.task.num_rounds + success = done and self.current_position == self.task.goal + reward = 1.0 - self.normalized_loss if success else 0.0 + return { + "path": path, + "normalized_loss": self.normalized_loss, + "done": done, + "success": success, + "reward": reward, + } + + def render(self) -> str: + """Render row and column labels with known costs and question marks.""" + header = " " + "".join(f"{col:>3}" for col in range(self.pack_state.size)) + rows = [header] + for row in range(self.pack_state.size): + cells = [] + for col in range(self.pack_state.size): + value = ( + str(int(self.pack_state.costs[row, col])) + if self.pack_state.revealed[row, col] + else "?" + ) + cells.append(f"{value:>3}") + rows.append(f"{row:>3} " + "".join(cells)) + return "\n".join(rows) diff --git a/trinity/common/workflows/connect_the_dots/grid_navigation/prompts/__init__.py b/trinity/common/workflows/connect_the_dots/grid_navigation/prompts/__init__.py new file mode 100644 index 00000000000..4612b8e3721 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/grid_navigation/prompts/__init__.py @@ -0,0 +1,54 @@ +"""Prompt loading helpers for the CoD grid-navigation workflow.""" + +from pathlib import Path +from typing import Optional, Tuple + +from jinja2 import Environment, FileSystemLoader + +PROMPTS_DIR = Path(__file__).parent + + +def get_jinja_env() -> Environment: + return Environment( + loader=FileSystemLoader(PROMPTS_DIR), + trim_blocks=True, + lstrip_blocks=True, + ) + + +def load_system_prompt(*, reveal_radius: int) -> str: + """Render the system prompt with the configured observation radius.""" + return ( + get_jinja_env() + .get_template("system.jinja2") + .render( + reveal_radius=reveal_radius, + ) + ) + + +def load_user_prompt( + *, + current_round: int, + max_rounds: int, + current_position: Tuple[int, int], + goal_position: Tuple[int, int], + observation: str, + action_feedback: Optional[str], +) -> str: + """Render one round's grid observation and task state.""" + return ( + get_jinja_env() + .get_template("user.jinja2") + .render( + current_round=current_round, + max_rounds=max_rounds, + current_position=current_position, + goal_position=goal_position, + observation=observation, + action_feedback=action_feedback, + ) + ) + + +__all__ = ["load_system_prompt", "load_user_prompt", "PROMPTS_DIR"] diff --git a/trinity/common/workflows/connect_the_dots/grid_navigation/prompts/system.jinja2 b/trinity/common/workflows/connect_the_dots/grid_navigation/prompts/system.jinja2 new file mode 100644 index 00000000000..1deaf7c15b1 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/grid_navigation/prompts/system.jinja2 @@ -0,0 +1,35 @@ +You are navigating a two-dimensional cost grid. Your goal is to finish the final round exactly at the target position while keeping the normalized navigation loss low. + +## Grid and observations +- Rows and columns use zero-based indices. +- Every cell has a fixed integer cost from 0 to 99. +- Unknown costs are shown as `?`. Observed costs are shown as integers. +- The current task belongs to a stream of tasks within the same environment. Every task in this stream shares the same fixed cost grid. +- After each move, the environment reveals every cell whose Chebyshev distance from an entered cell is at most {{ reveal_radius }}. These observations are retained automatically in later rounds and later tasks in the stream. + +## Movement rules +- The task has a fixed number of rounds, and you must take exactly one move per round. +- A move selects any other cell in the same row or the same column as your current position. +- You may cross any positive number of cells up to the grid boundary. +- The entered cells include every crossed cell and the destination, but not the starting cell of the move. +- Reaching the target before the final round does not end the task. You must continue moving and be at the target when the final round ends. + +## Loss and reward +Let V be the list of all entered cells over all rounds. Repeated visits appear in V repeatedly. The mean entered-cell cost and normalized loss are: + +mean_cost = sum(cost(v) for v in V) / |V| +loss = mean_cost / 100 = sum(cost(v) for v in V) / (100 * |V|) + +The loss depends on the mean cost, not the total cost. There is no separate penalty for path length or the number of entered cells, so entering fewer cells is not inherently better. A longer route can lower the final loss when the additional cells are cheaper than the running mean, while an expensive detour raises it. Do not default to a conventional shortest-path or minimum-total-cost objective. + +- If your position after the final round is exactly the target, reward = 1 - loss. +- Otherwise, reward = 0. +- An invalidly formatted or illegal move ends the task with reward 0. + +Explore broadly across the full grid over the stream of tasks instead of observing only a short corridor to the current target. As the map becomes more complete, exploit the structure of the cost grid to improve both the current route and later routes. + +## Response format +Think concisely about the move, then give exactly one destination. Format your response as follows: + +(your thinking process goes here) +row,col diff --git a/trinity/common/workflows/connect_the_dots/grid_navigation/prompts/user.jinja2 b/trinity/common/workflows/connect_the_dots/grid_navigation/prompts/user.jinja2 new file mode 100644 index 00000000000..a1fed2165e5 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/grid_navigation/prompts/user.jinja2 @@ -0,0 +1,14 @@ +Round {{ current_round }}/{{ max_rounds }} +{% if action_feedback %} + +{{ action_feedback }} +{% endif %} + +Current position: ({{ current_position[0] }},{{ current_position[1] }}) +Target position: ({{ goal_position[0] }},{{ goal_position[1] }}) + +Observed cost grid: +{{ observation }} + +Choose one legal destination for this round. +Remember to follow every requirement in the `## Movement rules` section. diff --git a/trinity/common/workflows/connect_the_dots/grid_navigation/workflow.py b/trinity/common/workflows/connect_the_dots/grid_navigation/workflow.py new file mode 100644 index 00000000000..240a11d2f90 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/grid_navigation/workflow.py @@ -0,0 +1,218 @@ +"""CoD multi-step workflow for the persistent-cost grid-navigation task.""" + +from __future__ import annotations + +import re +from typing import TYPE_CHECKING, List, Optional, Tuple + +from trinity.common.experience import Experience +from trinity.common.workflows.connect_the_dots.base_workflow import AsyncCoDMultiStepWorkflow +from trinity.common.workflows.connect_the_dots.grid_navigation.env import ( + GridNavigationEnv, + generate_task, + get_pack_state, +) +from trinity.common.workflows.connect_the_dots.grid_navigation.prompts import ( + load_system_prompt, + load_user_prompt, +) +from trinity.common.workflows.connect_the_dots.utils import extract_content_between_keys +from trinity.common.workflows.workflow import Task + +if TYPE_CHECKING: + from trinity.common.models.model import ModelWrapper + + +def parse_action(response: str) -> Optional[Tuple[int, int]]: + """Parse one row,column destination from the answer tags.""" + content, success = extract_content_between_keys(response, "", "") + if not success: + return None + match = re.fullmatch(r"\s*(-?\d+)\s*,\s*(-?\d+)\s*", content) + if match is None: + return None + return int(match.group(1)), int(match.group(2)) + + +class CoDGridNavigationWorkflow(AsyncCoDMultiStepWorkflow): + """Navigate a hidden persistent cost grid for an exact number of rounds.""" + + is_async: bool = True + can_reset: bool = True + + def __init__( + self, + model: ModelWrapper, + task: Task, + auxiliary_models: Optional[List] = None, + use_openai_client: bool = False, + ): + """Initialize the workflow and bind it to its pack state.""" + super().__init__( + model=model, + task=task, + auxiliary_models=auxiliary_models, + use_openai_client=use_openai_client, + ) + self.reset(task) + + def reset(self, task: Task): + """Reset task configuration and acquire the shared pack environment.""" + super().reset(task) + args = task.workflow_args + self.grid_min_size = int(args.get("grid_min_size", 12)) + self.grid_max_size = int(args.get("grid_max_size", 16)) + self.landscape_num_components = int(args.get("landscape_num_components", 6)) + self.landscape_min_scale = float(args.get("landscape_min_scale", 0.10)) + self.landscape_max_scale = float(args.get("landscape_max_scale", 0.35)) + self.min_rounds = int(args.get("min_rounds", 6)) + self.max_rounds = int(args.get("max_rounds", 10)) + self.reveal_radius = int(args.get("reveal_radius", 1)) + + self.seed = int(self.raw_task.get("seed", 42)) + self.pack_seed = int(self.raw_task.get("pack_seed", self.seed)) + self.task_idx = int(self.raw_task.get("task_idx", 0)) + + pack_state = get_pack_state( + pack_seed=self.pack_seed, + task_idx=self.task_idx, + grid_min_size=self.grid_min_size, + grid_max_size=self.grid_max_size, + landscape_num_components=self.landscape_num_components, + landscape_min_scale=self.landscape_min_scale, + landscape_max_scale=self.landscape_max_scale, + ) + navigation_task = generate_task( + task_seed=self.seed, + grid_size=pack_state.size, + min_rounds=self.min_rounds, + max_rounds=self.max_rounds, + ) + self.env = GridNavigationEnv( + pack_state=pack_state, + task=navigation_task, + reveal_radius=self.reveal_radius, + ) + + self.done = False + self.final_reward = 0.0 + self.current_step = 0 + self.action_feedback: Optional[str] = None + self.early_termination_by_format_issue = False + self.memory: List[dict] = [] + + def _build_system_prompt(self) -> str: + return self._augment_system_prompt(load_system_prompt(reveal_radius=self.reveal_radius)) + + async def run_async(self) -> List[Experience]: + """Reset task-local episode state and run all navigation rounds.""" + self.env.reset_task() + self.done = False + self.final_reward = 0.0 + self.current_step = 0 + self.action_feedback = None + self.early_termination_by_format_issue = False + + self.memory.clear() + self.memory.append({"role": "system", "content": self._build_system_prompt()}) + return await super().run_async() + + def _terminate_invalid_action(self, message: str) -> None: + self.action_feedback = f"Invalid action: {message} Game over." + self.done = True + self.early_termination_by_format_issue = True + + async def step_async(self, step_num: int) -> Tuple[bool, List[Experience]]: + """Prompt for, parse, and execute one grid move.""" + if self.done: + return False, [] + + user_content = load_user_prompt( + current_round=step_num + 1, + max_rounds=self.env.task.num_rounds, + current_position=self.env.current_position, + goal_position=self.env.task.goal, + observation=self.env.render(), + action_feedback=self.action_feedback, + ) + if self.icl_examples and step_num == 0: + user_content = ( + f"{user_content}\n\nHere are some reference examples:\n\n{self.icl_examples}" + ) + + self.memory.append({"role": "user", "content": user_content}) + if self.reply_prefix: + self.memory.append({"role": "assistant", "content": self.reply_prefix}) + + experiences = await self.model.chat_async(self.memory) + response_text = experiences[0].response_text + self.memory.append({"role": "assistant", "content": response_text}) + + sys_prompt = self.memory[0]["content"] + for exp in experiences: + exp.info["sys_prompt"] = sys_prompt + exp.info["user_prompt"] = user_content + + destination = parse_action(response_text) + if destination is None: + self._terminate_invalid_action( + "expected exactly one row,col destination." + ) + self.current_step = step_num + 1 + return False, experiences + + validation_error = self.env.validate_destination(destination) + if validation_error is not None: + self._terminate_invalid_action(validation_error) + self.current_step = step_num + 1 + return False, experiences + + result = self.env.step(destination) + path_text = " -> ".join(f"({row},{col})" for row, col in result["path"]) + self.action_feedback = ( + f"You moved to ({destination[0]},{destination[1]}) through {len(result['path'])} " + f"entered cells: {path_text}. " + f"Cumulative normalized loss: {result['normalized_loss']:.4f}." + ) + self.current_step = step_num + 1 + self.done = bool(result["done"]) + if self.done: + self.final_reward = float(result["reward"]) + + for exp in experiences: + exp.info["destination"] = destination + exp.info["normalized_loss"] = result["normalized_loss"] + + return not self.done, experiences + + def _build_trajectory(self) -> str: + trajectory = super()._build_trajectory() + final_state = ( + "Final environment state:\n" + f"Position: {self.env.current_position}\n" + f"Target: {self.env.task.goal}\n" + f"Normalized loss: {self.env.normalized_loss:.4f}\n" + f"Observed cost grid:\n{self.env.render()}" + ) + return f"{trajectory}\n\n{final_state}" if trajectory else final_state + + def _get_feedback(self) -> str: + if self.early_termination_by_format_issue: + outcome = self.action_feedback or "Invalid action." + elif self.env.current_position == self.env.task.goal: + outcome = ( + f"Success: finished round {self.env.task.num_rounds} at the target. " + f"Normalized loss: {self.env.normalized_loss:.4f}; " + f"reward: {self.final_reward:.4f}." + ) + else: + outcome = ( + f"Failed: after {self.env.task.num_rounds} rounds, position " + f"{self.env.current_position} did not equal target {self.env.task.goal}. Reward: 0." + ) + return f"{outcome}\n\nFinal observed cost grid:\n{self.env.render()}" + + @property + def max_step_num(self) -> int: + """Return the task's exact number of rounds.""" + return self.env.task.num_rounds diff --git a/trinity/common/workflows/connect_the_dots/learn2ask/__init__.py b/trinity/common/workflows/connect_the_dots/learn2ask/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/trinity/common/workflows/connect_the_dots/learn2ask/prompts/__init__.py b/trinity/common/workflows/connect_the_dots/learn2ask/prompts/__init__.py new file mode 100644 index 00000000000..fd27f28b7af --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/learn2ask/prompts/__init__.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +"""Prompt management for CoD Learn2Ask workflow using Jinja2 templates. + +Two templates live here: + +* system.jinja2 — rollout system prompt. A single template covers + both training modes: the `` guideline is emitted when + train_mode != "Ra", matching the original rollout_prompt_med / + rollout_prompt_med_Ra split in + examples/learn_to_ask/workflow/prompt_learn2ask.py byte-for-byte. + +* reward_judge.jinja2 — judge system prompt fed to the auxiliary + model. Holds a single placeholder, info_truth, that Trinity fills + per-cid. Renders byte-identically to + reward_prompt_med.format(info_truth). +""" + +from pathlib import Path + +from jinja2 import Environment, FileSystemLoader + + +PROMPTS_DIR = Path(__file__).parent + + +def get_jinja_env() -> Environment: + """Get Jinja2 environment with template loader.""" + return Environment( + loader=FileSystemLoader(PROMPTS_DIR), + trim_blocks=True, + lstrip_blocks=True, + ) + + +def load_system_prompt(train_mode: str = "Ra+Rs", **kwargs) -> str: + """Render the rollout system prompt. + + Args: + train_mode: "Ra+Rs" / "Rs" / "Ra" — "Ra" drops the + instruction because the decision signal is not part of Ra's + reward. All other values emit it. + **kwargs: extra variables forwarded to the template. + """ + env = get_jinja_env() + template = env.get_template("system.jinja2") + return template.render(train_mode=train_mode, **kwargs) + + +def load_reward_judge_prompt(info_truth: str, **kwargs) -> str: + """Render the judge system prompt for the current cid. + + Args: + info_truth: comma-separated structured symptom points extracted + by data prep step 1/2 (same string the upstream Learn2Ask + reward_fn passes to reward_prompt_med.format). + **kwargs: extra variables forwarded to the template. + """ + env = get_jinja_env() + template = env.get_template("reward_judge.jinja2") + return template.render(info_truth=info_truth, **kwargs) + + +__all__ = [ + "load_system_prompt", + "load_reward_judge_prompt", + "PROMPTS_DIR", +] diff --git a/trinity/common/workflows/connect_the_dots/learn2ask/prompts/reward_judge.jinja2 b/trinity/common/workflows/connect_the_dots/learn2ask/prompts/reward_judge.jinja2 new file mode 100644 index 00000000000..0d25f9b750b --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/learn2ask/prompts/reward_judge.jinja2 @@ -0,0 +1,29 @@ + +# Task +You are an evaluation assistant. The user will provide a dialogue history between a doctor and a patient. You must analyze the dialogue and evaluate the doctor's last message. + +# Grading Policy +## Format Score +- 1.0: The doctor's last message contains exactly **one question**. +- 0.5: The doctor's last message contains **two questions**. +- 0.0: The doctor's last message contains **three or more questions**. + +## Content Score +- 1.0: The question(s) **directly ask about** any item in the Reference Information. +- 0.5: The question(s) are **highly relevant** to, but not directly asking about, any item in the Reference Information. +- 0.0: The question(s) are **irrelevant** to all items in the Reference Information. + +# Reference Information +{{ info_truth }} + +# Output Format + +After reasoning, respond with exactly three bracketed blocks, in this order: + +[format_score]Insert only the format score as a float, one of 1.0, 0.5, 0.0.[/format_score] +[content_score]Insert only the content score as a float, one of 1.0, 0.5, 0.0.[/content_score] +[feedback]Insert one short abstract sentence on the response's strengths or gaps, grounded in the grading policy above. Do NOT include any specific term from the Reference Information. Keep on a single line.[/feedback] + +Important: +- The three bracketed blocks above must appear in your final answer, in order. +- Scores must be based only on the doctor's last message and the provided Reference Information. diff --git a/trinity/common/workflows/connect_the_dots/learn2ask/prompts/system.jinja2 b/trinity/common/workflows/connect_the_dots/learn2ask/prompts/system.jinja2 new file mode 100644 index 00000000000..42e9f1fa5d1 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/learn2ask/prompts/system.jinja2 @@ -0,0 +1,11 @@ + +# Task +You are a medical assistant. Your task is to understand the ongoing conversation and continue the medical inquiry in English. + +## Guidelines +- Each response must contain exactly one clear and concise medical question with 2 to 3 answer choices. +- Do not repeat any previous question. +- Your response must be a single sentence. +{% if train_mode != "Ra" %} +- If enough information has been gathered to make a medication suggestion, output only: +{% endif %} diff --git a/trinity/common/workflows/connect_the_dots/learn2ask/workflow.py b/trinity/common/workflows/connect_the_dots/learn2ask/workflow.py new file mode 100644 index 00000000000..063459afec9 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/learn2ask/workflow.py @@ -0,0 +1,369 @@ +# -*- coding: utf-8 -*- +""" +CoD (Connect-the-Dots) workflow for the Learn2Ask medical inquiry task. + +Single-turn workflow adapted from examples/learn_to_ask/workflow/ +workflow_learn2ask.py. Uses an auxiliary model as LLM judge (Qwen judge +from the Learn2Ask paper) for content/format scoring; action score is a +hard rule based on detection. On top of the original workflow +this module surfaces trajectory / feedback into exp.info so the CoD +meta-workflow can consume them for cross-task hint learning. +""" + +from __future__ import annotations + +import asyncio +import re +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple + +from trinity.common.experience import Experience +from trinity.common.workflows.connect_the_dots.base_workflow import ( + AsyncCoDMultiStepWorkflow, +) +from trinity.common.workflows.connect_the_dots.learn2ask.prompts import ( + load_reward_judge_prompt, + load_system_prompt, +) +from trinity.common.workflows.workflow import ( + Task, + log_sys_user_prompts_in_exp, +) + +if TYPE_CHECKING: + import openai + + from trinity.common.models.model import ModelWrapper + + +# --- Tag parsing for judge output --- + +_TAG_PATTERN = re.compile(r"\[(\w+)\](.*?)\[/\1\]", re.DOTALL) +_THINK_PATTERN = re.compile(r".*?", re.DOTALL) + + +def _parse_tag_string(text: str) -> Dict[str, str]: + """Parse [tag]...[/tag] pairs, ignoring blocks.""" + return { + tag: value.strip() + for tag, value in _TAG_PATTERN.findall(_THINK_PATTERN.sub("", text)) + } + + +# --- Dialogue formatting helpers --- + +def _merge_dialogue(msg_list: List[dict]) -> str: + """Render a list of {role, content} messages as 'patient:' / 'doctor:' lines.""" + lines = [] + for msg in msg_list: + role = msg.get("role") + content = msg.get("content", "") + if role == "user": + lines.append(f"patient: {content}") + elif role == "assistant": + lines.append(f"doctor: {content}") + return "\n".join(lines) + + +def _merge_consecutive_same_role(messages: List[dict]) -> List[dict]: + """Collapse consecutive same-role messages by joining contents with newlines.""" + if not messages: + return messages + merged = [dict(messages[0])] + for msg in messages[1:]: + if msg.get("role") == merged[-1].get("role"): + merged[-1]["content"] = f"{merged[-1]['content']}\n{msg['content']}" + else: + merged.append(dict(msg)) + return merged + + +class CoDLearn2AskWorkflow(AsyncCoDMultiStepWorkflow): + """Learn2Ask single-turn workflow wired into the CoD meta-workflow. + + Reward stays aligned with the original Learn2Ask paper (LLM judge for + content/format, hard-rule action gating, fusion formula). The main + CoD-side additions are: + + * `set_hint / set_icl_examples / set_max_response_tokens_restraint` + to receive the CoD context between tasks. + * `exp.info["trajectory"]` and `exp.info["feedback"]` populated for + the iterative hint generator to consume. + """ + + @property + def max_step_num(self) -> int: + return 1 + + def __init__( + self, + *, + task: Task, + model: "ModelWrapper", + auxiliary_models: Optional[List["ModelWrapper"]] = None, + use_openai_client: bool = False, + ): + assert ( + auxiliary_models is not None and len(auxiliary_models) == 1 + ), "CoDLearn2AskWorkflow expects exactly one auxiliary model (judge)." + super().__init__( + task=task, + model=model, + auxiliary_models=auxiliary_models, + use_openai_client=use_openai_client, + ) + self.reset(task) + + # ------------------------------------------------------------------ + # Lifecycle: reset / CoD setters + # ------------------------------------------------------------------ + + def reset(self, task: Task): + """Reset per-task state.""" + super().reset(task) + + workflow_args = task.workflow_args or {} + self.train_mode: str = workflow_args.get("train_mode", "Ra+Rs") + self.fusion_mode: str = workflow_args.get("fusion_mode", "default") + + if isinstance(self.task_desc, list): + self.task_desc = _merge_consecutive_same_role(self.task_desc) + + # Ground truth (from predata) + self.action_truth: str = self.raw_task.get("decision_truth", "continue") + self.info_truth: str = self.raw_task.get("info_truth", "") + self.session_id = str(self.raw_task.get("session_id", "")) + self.diagn: str = self.raw_task.get("diagn", "") + + self.system_prompt: str = load_system_prompt(train_mode=self.train_mode) + + # ------------------------------------------------------------------ + # Prompt assembly + # ------------------------------------------------------------------ + + def format_messages(self) -> List[dict]: + sys_prompt = self._augment_system_prompt(self.system_prompt) + if isinstance(self.task_desc, list): + messages = [{"role": "system", "content": sys_prompt}] + self.task_desc + elif isinstance(self.task_desc, str): + messages = [ + {"role": "system", "content": sys_prompt}, + {"role": "user", "content": self.task_desc}, + ] + else: + raise ValueError( + f"task.task_desc must be a list of messages or a str, got {type(self.task_desc)}" + ) + if self.reply_prefix: + messages.append({"role": "assistant", "content": self.reply_prefix}) + return messages + + # ------------------------------------------------------------------ + # Main execution + # ------------------------------------------------------------------ + + async def run_async(self) -> List[Experience]: + messages = self.format_messages() + responses = await self.model.chat_async(messages, **self.rollout_args) + + # Score every response in parallel so concurrent judge calls overlap. + score_results = await asyncio.gather( + *(self._compute_reward(r.response_text or "") for r in responses) + ) + + history_text = self._history_as_dialogue() + task_desc_str = history_text # single-turn: the prompt dialogue is the task + + for response, (reward, component_scores, judge_feedback, judge_format_error) in zip( + responses, score_results + ): + response.reward = reward + if response.metrics is None: + response.metrics = {} + # Skip per-component scores when judge failed — they're 0/0/0 placeholders, not real signal. + if not judge_format_error: + response.metrics.update(component_scores) + + resp_text = response.response_text or "" + response.info["task_desc"] = task_desc_str + response.info["trajectory"] = ( + f"# Task instructions given to the assistant\n{self.system_prompt}\n\n" + f"# Resulting conversation\n{history_text}\ndoctor: {resp_text}" + if history_text else + f"# Task instructions given to the assistant\n{self.system_prompt}\n\n" + f"# Resulting conversation\ndoctor: {resp_text}" + ) + response.info["feedback"] = self._build_feedback( + component_scores, resp_text, judge_feedback + ) + if judge_format_error: + response.info["judge_format_error"] = True + + log_sys_user_prompts_in_exp(messages, responses) + return responses + + # ------------------------------------------------------------------ + # Reward computation (mirrors examples/learn_to_ask reward_fn) + # ------------------------------------------------------------------ + + async def _compute_reward( + self, response: str + ) -> Tuple[float, Dict[str, float], str, bool]: + action_response = "stop" if "" in response else "continue" + judge_feedback = "" + judge_format_error = False + + if self.action_truth != action_response: + action_score = format_score = content_score = 0.0 + else: + action_score = 1.0 + if self.action_truth == "continue": + score_dict = await self._llm_judge(response) + try: + format_score = float(score_dict.get("format_score")) + content_score = float(score_dict.get("content_score")) + except (TypeError, ValueError): + judge_format_error = True + format_score, content_score = 0.0, 0.0 + judge_feedback = (score_dict.get("feedback") or "").strip() + else: + content_score = 1.0 + format_score = 1.0 if response.strip() == "" else 0.0 + + final_reward = self._fuse_scores(action_score, content_score, format_score) + metrics = { + "action_score": action_score, + "content_score": content_score, + "format_score": format_score, + } + return final_reward, metrics, judge_feedback, judge_format_error + + def _fuse_scores( + self, action_score: float, content_score: float, format_score: float + ) -> float: + if self.train_mode == "Ra+Rs": + if self.fusion_mode == "sum": + return action_score + content_score + format_score + return action_score * (1 + 2 * content_score) + format_score + if self.train_mode == "Ra": + return 2 * content_score + format_score + # "Rs" + return action_score * 3 + format_score + + async def _llm_judge( + self, response: str, max_retries: int = 5 + ) -> Dict[str, str]: + """Call the auxiliary judge model with the dialogue + candidate reply. + + Matches examples/learn_to_ask/workflow/llm_reward's retry loop: + one initial attempt plus `max_retries` retries (total of + max_retries + 1 = 6 tries), sleeping attempt-count seconds + between tries. + """ + client: "openai.AsyncOpenAI" = self.auxiliary_models[0] + history = self._history_as_dialogue() + judge_user = ( + f"{history}\ndoctor: {response}\n" if history else f"doctor: {response}\n" + ) + judge_messages = [ + {"role": "system", "content": load_reward_judge_prompt(info_truth=self.info_truth)}, + {"role": "user", "content": judge_user}, + ] + + total_tries = max_retries + 1 + for attempt in range(total_tries): + try: + completion = await client.chat.completions.create( + model=client.model_path, + messages=judge_messages, + stream=False, + temperature=1.0, + top_p=0.95, + presence_penalty=0.0, + extra_body={ + "chat_template_kwargs": {"enable_thinking": True}, + "top_k": 20, + "min_p": 0.0, + "repetition_penalty": 1.0, + }, + ) + content = completion.choices[0].message.content or "" + return _parse_tag_string(content) + except Exception: + if attempt >= total_tries - 1: + return {} + await asyncio.sleep(1.0 * (attempt + 1)) + return {} + + # ------------------------------------------------------------------ + # Auxiliary artefacts for CoD hint generation + # ------------------------------------------------------------------ + + def _history_as_dialogue(self) -> str: + """Render task_desc (observed context) as patient/doctor dialogue.""" + if isinstance(self.task_desc, list): + return _merge_dialogue(self.task_desc) + if isinstance(self.task_desc, str): + return f"patient: {self.task_desc}" + return "" + + def _build_feedback( + self, component_scores: Dict[str, float], response: str, judge_feedback: str = "" + ) -> str: + """Human-readable feedback summarising the judge + action outcome.""" + action_s = component_scores["action_score"] + content_s = component_scores["content_score"] + format_s = component_scores["format_score"] + got = "stop" if "" in response else "continue" + total = self._fuse_scores(action_s, content_s, format_s) + max_reward = self._fuse_scores(1.0, 1.0, 1.0) + + parts = [ + f"Reward {total:.1f}/{max_reward:.1f}. Subscores: action={action_s:.0f}, " + f"content={content_s:.1f}, format={format_s:.1f}." + ] + + if action_s == 0: + if self.action_truth == "continue": + parts.append( + "Action: chose 'stop' but expected 'continue'. This is premature " + "termination. The patient still has unfilled symptom dimensions " + "worth probing; a focused follow-up question targeting one of them " + f"would have scored up to {max_reward:.1f}. action=0 zeros all " + "subscores. The big " + "penalty for stopping early signals that information sufficiency is " + "the primary stop criterion." + ) + else: + parts.append( + "Action: chose 'continue' and asked a question but expected 'stop'. " + "This is a redundant question. Critical symptom info has already been " + "gathered, so the right action was to emit '' for a " + f"{max_reward:.1f} reward. " + "action=0 zeros all subscores. The penalty signals that over-asking " + "after enough info is collected is as costly as a wrong question." + ) + return " ".join(parts) + + parts.append(f"Action: '{got}' matched expected. action=1.0.") + + if self.action_truth == "stop": + if format_s > 0: + parts.append("Format=1.0: response was clean ''. Content auto-1.0.") + else: + parts.append( + "Format=0.0: response had extra characters around '', " + "losing 1.0. Content auto-1.0." + ) + else: + parts.append( + f"Format={format_s:.1f}. Judge penalizes multi-question turns: " + f"1 question scores 1.0, 2 scores 0.5, 3 or more scores 0.0." + ) + parts.append( + f"Content={content_s:.1f}. Judge rates how directly the question targets " + f"an unfilled symptom dimension: direct=1.0, relevant=0.5, irrelevant=0.0." + ) + if judge_feedback: + parts.append(f"Judge note: {judge_feedback}") + + return " ".join(parts) diff --git a/trinity/common/workflows/connect_the_dots/optimalcontrol/__init__.py b/trinity/common/workflows/connect_the_dots/optimalcontrol/__init__.py new file mode 100644 index 00000000000..b209d0bca74 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/optimalcontrol/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +"""CoD (Connect-the-Dots) workflow for the optimal-control deployment environment.""" + +from trinity.common.workflows.connect_the_dots.optimalcontrol.env import ( + ActionFn, + OptimalControlEnv, +) +from trinity.common.workflows.connect_the_dots.optimalcontrol.prompts import ( + load_system_prompt, + load_user_prompt, +) +from trinity.common.workflows.connect_the_dots.optimalcontrol.workflow import ( + CoDOptimalControlWorkflow, +) + +__all__ = [ + "CoDOptimalControlWorkflow", + "OptimalControlEnv", + "ActionFn", + "load_system_prompt", + "load_user_prompt", +] diff --git a/trinity/common/workflows/connect_the_dots/optimalcontrol/env.py b/trinity/common/workflows/connect_the_dots/optimalcontrol/env.py new file mode 100644 index 00000000000..06d601a5dde --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/optimalcontrol/env.py @@ -0,0 +1,516 @@ +# -*- coding: utf-8 -*- +"""Optimal-control environment with hidden linear dynamics. + +State: s_t = (x_t, v_t) +Dynamics: + v_{t+1} = a_env * v_t + b_env * u_t + epsilon_t + x_{t+1} = x_t + v_{t+1} +Control constraint: u_t in [-1, 1] +""" + +import ast +import math +import operator +import random +from typing import Any, Callable, Dict, List, Tuple, TypedDict +from xml.sax.saxutils import escape + +ActionFn = Callable[[float, float, int, float, float], float] + + +class ResolvedOptimalControlTask(TypedDict): + a_env: float + b_env: float + min_abs_b_env: float #!!! + x0: float + v0: float + x_target: float + v_target: float + horizon: int + control_penalty_coef: float + enable_process_noise: bool #!!! + process_noise_std: float #!!! + task_seed: int #!!! + + +def _clip(value: float, lower: float, upper: float) -> float: + """Clip value to [lower, upper].""" + return max(lower, min(value, upper)) + + +def format_reward(value: float) -> str: + """Keep three decimals unless a positive reward would appear to be zero.""" + rounded = f"{value:.3f}" + if value > 0.0 and rounded == "0.000": + return f"{value:.3e} (nonzero; below three-decimal precision)" + return rounded + + +_VARIABLE_NAMES = { + "x", + "v", + "t", + "x_target", + "v_target", + "horizon", + "remaining_steps", +} +_BINARY_OPERATORS = { + ast.Add: operator.add, + ast.Sub: operator.sub, + ast.Mult: operator.mul, + ast.Div: operator.truediv, + ast.Pow: operator.pow, +} +_UNARY_OPERATORS = { + ast.UAdd: operator.pos, + ast.USub: operator.neg, + ast.Not: operator.not_, +} +_COMPARISON_OPERATORS = { + ast.Eq: operator.eq, + ast.NotEq: operator.ne, + ast.Lt: operator.lt, + ast.LtE: operator.le, + ast.Gt: operator.gt, + ast.GtE: operator.ge, +} +_FUNCTION_COMPARISONS = { + "lt": operator.lt, + "le": operator.le, + "gt": operator.gt, + "ge": operator.ge, + "eq": operator.eq, + "ne": operator.ne, +} +_FUNCTION_ARITY = { + "abs": (1, 1), + "min": (1, None), + "max": (1, None), + "pow": (2, 2), + "clip": (3, 3), + "ifelse": (3, 3), + "lt": (2, 2), + "le": (2, 2), + "gt": (2, 2), + "ge": (2, 2), + "eq": (2, 2), + "ne": (2, 2), +} + + +def _error_detail(error: Exception) -> str: + """Return a concise error description suitable for model feedback.""" + return f"{type(error).__name__}: {error}"[:500] + + +def _validate_expression_node(node: ast.AST) -> None: + """Validate the supported scalar expression language.""" + if isinstance(node, ast.Expression): + _validate_expression_node(node.body) + return + if isinstance(node, ast.Constant): + if isinstance(node.value, bool) or not isinstance(node.value, (int, float)): + raise ValueError("only numeric constants are allowed") + try: + finite = math.isfinite(float(node.value)) + except OverflowError as error: + raise ValueError("numeric constants must be finite") from error + if not finite: + raise ValueError("numeric constants must be finite") + return + if isinstance(node, ast.Name): + if node.id not in _VARIABLE_NAMES: + raise ValueError(f"unknown variable: {node.id}") + return + if isinstance(node, ast.BinOp): + if type(node.op) not in _BINARY_OPERATORS: + raise ValueError("unsupported binary operator") + _validate_expression_node(node.left) + _validate_expression_node(node.right) + return + if isinstance(node, ast.UnaryOp): + if type(node.op) not in _UNARY_OPERATORS: + raise ValueError("unsupported unary operator") + _validate_expression_node(node.operand) + return + if isinstance(node, ast.Call): + if not isinstance(node.func, ast.Name) or node.func.id not in _FUNCTION_ARITY: + raise ValueError("unsupported function call") + if node.keywords: + raise ValueError("keyword arguments are not allowed") + minimum, maximum = _FUNCTION_ARITY[node.func.id] + if len(node.args) < minimum or (maximum is not None and len(node.args) > maximum): + raise ValueError(f"invalid argument count for {node.func.id}") + for argument in node.args: + _validate_expression_node(argument) + return + if isinstance(node, ast.IfExp): + _validate_expression_node(node.test) + _validate_expression_node(node.body) + _validate_expression_node(node.orelse) + return + if isinstance(node, ast.Compare): + if any(type(comparator) not in _COMPARISON_OPERATORS for comparator in node.ops): + raise ValueError("unsupported comparison operator") + _validate_expression_node(node.left) + for comparator in node.comparators: + _validate_expression_node(comparator) + return + if isinstance(node, ast.BoolOp): + if not isinstance(node.op, (ast.And, ast.Or)): + raise ValueError("unsupported boolean operator") + for value in node.values: + _validate_expression_node(value) + return + raise ValueError(f"unsupported expression syntax: {type(node).__name__}") + + +def validate_action_expression(expression: object) -> str: + """Parse and validate one controller expression.""" + if not isinstance(expression, str): + raise ValueError("action_expression must be text") + expression = expression.strip() + if not expression: + raise ValueError("action_expression cannot be empty") + try: + tree = ast.parse(expression, mode="eval") + _validate_expression_node(tree) + except SyntaxError as error: + raise ValueError(f"invalid action_expression syntax: {error.msg}") from error + except RecursionError as error: + raise ValueError("action_expression is too deeply nested") from error + return expression + + +def _evaluate_expression(node: ast.AST, variables: Dict[str, float]) -> Any: + """Evaluate one previously validated expression node.""" + if isinstance(node, ast.Constant): + return float(node.value) + if isinstance(node, ast.Name): + return variables[node.id] + if isinstance(node, ast.BinOp): + return _BINARY_OPERATORS[type(node.op)]( + _evaluate_expression(node.left, variables), + _evaluate_expression(node.right, variables), + ) + if isinstance(node, ast.UnaryOp): + return _UNARY_OPERATORS[type(node.op)](_evaluate_expression(node.operand, variables)) + if isinstance(node, ast.Call): + function_name = node.func.id + if function_name == "ifelse": + condition = _evaluate_expression(node.args[0], variables) + branch = node.args[1] if condition else node.args[2] + return _evaluate_expression(branch, variables) + values = [_evaluate_expression(argument, variables) for argument in node.args] + if function_name == "abs": + return abs(values[0]) + if function_name == "min": + return min(values) + if function_name == "max": + return max(values) + if function_name == "pow": + return pow(values[0], values[1]) + if function_name == "clip": + return _clip(values[0], values[1], values[2]) + return _FUNCTION_COMPARISONS[function_name](values[0], values[1]) + if isinstance(node, ast.IfExp): + branch = node.body if _evaluate_expression(node.test, variables) else node.orelse + return _evaluate_expression(branch, variables) + if isinstance(node, ast.Compare): + left = _evaluate_expression(node.left, variables) + for operation, comparator in zip(node.ops, node.comparators): + right = _evaluate_expression(comparator, variables) + if not _COMPARISON_OPERATORS[type(operation)](left, right): + return False + left = right + return True + if isinstance(node, ast.BoolOp): + if isinstance(node.op, ast.And): + result = _evaluate_expression(node.values[0], variables) + for value in node.values[1:]: + if not result: + return result + result = _evaluate_expression(value, variables) + return result + result = _evaluate_expression(node.values[0], variables) + for value in node.values[1:]: + if result: + return result + result = _evaluate_expression(value, variables) + return result + raise TypeError(f"unsupported expression node: {type(node).__name__}") + + +def compile_action_expression(expression: str, horizon: int) -> Tuple[ActionFn | None, str | None]: + """Compile one validated scalar expression into a feedback action.""" + try: + expression = validate_action_expression(expression) + tree = ast.parse(expression, mode="eval") + except ValueError as error: + return None, str(error) + + def action(x, v, t, x_target, v_target): + variables = { + "x": float(x), + "v": float(v), + "t": float(t), + "x_target": float(x_target), + "v_target": float(v_target), + "horizon": float(horizon), + "remaining_steps": float(max(1, horizon - t)), + } + return _evaluate_expression(tree.body, variables) + + return action, None + + +def serialize_action_expression_xml(expression: str) -> str: + """Serialize a validated expression as canonical controller XML.""" + expression = validate_action_expression(expression) + return ( + "\n" + " \n" + f" {escape(expression)}\n" + " \n" + "" + ) + + +class OptimalControlEnv: + """One-dimensional optimal-control environment used by CoD-Deploy demos.""" + + @staticmethod #!!! + def _sample_b_env( #!!! + rng: random.Random, lower: float, upper: float, min_abs: float #!!! + ) -> float: #!!! + """Sample uniformly while excluding (-min_abs, min_abs).""" #!!! + if lower > upper: #!!! + raise ValueError("b_env_range lower bound must not exceed upper bound") #!!! + if min_abs < 0.0: #!!! + raise ValueError("min_abs_b_env must be non-negative") #!!! + if min_abs == 0.0: #!!! + return rng.uniform(lower, upper) #!!! + left_upper = min(upper, -min_abs) #!!! + right_lower = max(lower, min_abs) #!!! + left_length = max(0.0, left_upper - lower) #!!! + right_length = max(0.0, upper - right_lower) #!!! + total_length = left_length + right_length #!!! + if total_length == 0.0: #!!! + if lower <= -min_abs <= upper: #!!! + return -min_abs #!!! + if lower <= min_abs <= upper: #!!! + return min_abs #!!! + raise ValueError("b_env_range contains no value satisfying min_abs_b_env") #!!! + offset = rng.random() * total_length #!!! + if offset < left_length: #!!! + return lower + offset #!!! + return right_lower + (offset - left_length) #!!! + + @staticmethod + def resolve_task( + raw_task: Dict[str, Any], + workflow_args: Dict[str, Any], + ) -> ResolvedOptimalControlTask: + """Resolve one task against its runtime CoD pack environment.""" + environment_seed = int(raw_task.get("pack_seed", raw_task["seed"])) + rng = random.Random(environment_seed) + a_env = rng.uniform(*workflow_args["a_env_range"]) + b_lower, b_upper = map(float, workflow_args["b_env_range"]) #!!! + min_abs_b_env = float(workflow_args.get("min_abs_b_env", 0.0)) #!!! + b_env = OptimalControlEnv._sample_b_env( #!!! + rng, b_lower, b_upper, min_abs_b_env #!!! + ) #!!! + x0 = float(raw_task["x0"]) + v0 = float(raw_task["v0"]) + x_target = float(raw_task["x_target"]) + v_target = float(raw_task["v_target"]) + horizon_value = raw_task.get("horizon") #!!! + if horizon_value is None: #!!! + horizon_value = raw_task["max_horizon"] #!!! + horizon = int(horizon_value) #!!! + control_penalty_coef = float(raw_task["control_penalty_coef"]) + enable_process_noise = bool( #!!! + workflow_args.get("enable_process_noise", False) #!!! + ) #!!! + process_noise_std = float(workflow_args.get("process_noise_std", 0.0)) #!!! + if process_noise_std < 0.0: #!!! + raise ValueError("process_noise_std must be non-negative") #!!! + task_seed = int(raw_task["seed"]) #!!! + + return { + "a_env": a_env, + "b_env": b_env, + "min_abs_b_env": min_abs_b_env, #!!! + "x0": x0, + "v0": v0, + "x_target": x_target, + "v_target": v_target, + "horizon": horizon, + "control_penalty_coef": control_penalty_coef, + "enable_process_noise": enable_process_noise, #!!! + "process_noise_std": process_noise_std, #!!! + "task_seed": task_seed, #!!! + } + + def __init__( + self, + a_env: float, + b_env: float, + x0: float, + v0: float, + x_target: float, + v_target: float, + horizon: int = 8, + control_penalty_coef: float = 0.03, + enable_process_noise: bool = False, #!!! + process_noise_std: float = 0.0, #!!! + task_seed: int = 0, #!!! + ): + self.a_env = float(a_env) + self.b_env = float(b_env) + self.x0 = float(x0) + self.v0 = float(v0) + self.x_target = float(x_target) + self.v_target = float(v_target) + self.horizon = int(horizon) + self.control_penalty_coef = float(control_penalty_coef) + self.enable_process_noise = bool(enable_process_noise) #!!! + self.process_noise_std = float(process_noise_std) #!!! + if self.process_noise_std < 0.0: #!!! + raise ValueError("process_noise_std must be non-negative") #!!! + self.task_seed = int(task_seed) #!!! + + def reset(self) -> Tuple[float, float]: + """Reset to initial state.""" + return self.x0, self.v0 + + def step( #!!! + self, x: float, v: float, u: float, process_noise: float = 0.0 #!!! + ) -> Tuple[float, float]: #!!! + """Apply one control and return the next state.""" + u = float(_clip(u, -1.0, 1.0)) + v_next = self.a_env * v + self.b_env * u + float(process_noise) #!!! + x_next = x + v_next + return x_next, v_next + + def rollout(self, action_fn: ActionFn) -> Dict[str, Any]: + """Roll out the policy for the full horizon. + + Args: + action_fn: callable action(x, v, t, x_target, v_target) -> u_t. + + Returns: + dict with rollout states, controls, terminal metrics, and error status. #!!! + """ + x, v = self.reset() + xs = [x] + vs = [v] + us: List[float] = [] + process_noises: List[float] = [] #!!! + noise_rng = random.Random(self.task_seed ^ 0x5DEECE66D) #!!! + + for t in range(self.horizon): + try: + raw_u = action_fn(x, v, t, self.x_target, self.v_target) #!!! + if isinstance(raw_u, bool) or not isinstance(raw_u, (int, float)): #!!! + raise TypeError("action must return an int or float scalar") #!!! + u = float(raw_u) #!!! + if not math.isfinite(u): + raise ValueError("action must return a finite scalar") + u = _clip(u, -1.0, 1.0) + except Exception as error: + raise RuntimeError(f"action failed at t={t}: {_error_detail(error)}") from error + us.append(u) + process_noise = ( #!!! + noise_rng.gauss(0.0, self.process_noise_std) #!!! + if self.enable_process_noise and self.process_noise_std > 0.0 #!!! + else 0.0 #!!! + ) #!!! + process_noises.append(process_noise) #!!! + x, v = self.step(x, v, u, process_noise=process_noise) #!!! + xs.append(x) + vs.append(v) + + loss = self.compute_loss(xs[-1], vs[-1], us) #!!! + reward = self.compute_reward(loss) #!!! + process_noise_rms = ( #!!! + math.sqrt( #!!! + sum(noise * noise for noise in process_noises) / len(process_noises) #!!! + ) #!!! + if process_noises #!!! + else 0.0 #!!! + ) #!!! + + return { + "xs": xs, + "vs": vs, + "us": us, + "process_noises": process_noises, #!!! + "process_noise_rms": process_noise_rms, #!!! + "x_final": xs[-1], + "v_final": vs[-1], + "loss": loss, + "reward": reward, + } + + def rollout_action_expression( + self, + expression: str, + ) -> Tuple[Dict[str, Any] | None, str, str | None]: + """Compile and fully evaluate one feedback expression.""" + action_fn, validation_error = compile_action_expression(expression, self.horizon) + if action_fn is None: + return None, "format_error", validation_error + try: + return self.rollout(action_fn), "ok", None + except Exception as error: + return None, "action_error", _error_detail(error) + + def compute_loss( #!!! + self, x_final: float, v_final: float, us: List[float] #!!! + ) -> float: #!!! + """Compute terminal-state and control-effort loss.""" #!!! + pos_error = x_final - self.x_target #!!! + vel_error = v_final - self.v_target #!!! + control_cost = self.control_penalty_coef * sum(u * u for u in us) #!!! + return pos_error * pos_error + 2.0 * vel_error * vel_error + control_cost #!!! + + def compute_reward(self, loss: float) -> float: + """Convert loss to reward.""" + return 1.0 / (1.0 + loss) + + def render_trajectory(self, result: Dict[str, Any]) -> str: + """Render rollout result as a table aligned by time step. + + The control column u_t shows the control applied *at* step t while the + system is in state (x_t, v_t). At the terminal step T no further control + is available, so it is marked as ``--``. + """ + xs = result["xs"] + vs = result["vs"] + us = result["us"] + lines = [ + f"{'t':>3} {'x_t':>10} {'v_t':>10} {'u_t':>10}", + f"{0:>3} {xs[0]:>10.3f} {vs[0]:>10.3f} {us[0]:>10.3f}", + ] + for t in range(1, self.horizon): + u_str = f"{us[t]:>10.3f}" + lines.append(f"{t:>3} {xs[t]:>10.3f} {vs[t]:>10.3f} {u_str}") + lines.append( + f"{self.horizon:>3} {xs[self.horizon]:>10.3f} {vs[self.horizon]:>10.3f} {'--':>10}" + ) + return "\n".join(lines) + + def render_summary(self, result: Dict[str, Any]) -> str: + """Render the terminal state, target, loss, and reward.""" + reward_text = format_reward(float(result["reward"])) + lines = [ + "Terminal state:", + f"x_{self.horizon} = {result['x_final']:.3f}, v_{self.horizon} = {result['v_final']:.3f}.", + "Target:", + f"x_target = {self.x_target:.3f}, v_target = {self.v_target:.3f}.", + "Loss and reward:", + f"L = {result['loss']:.3f}, R = {reward_text}.", + ] + return "\n".join(lines) diff --git a/trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/__init__.py b/trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/__init__.py new file mode 100644 index 00000000000..1ecae727285 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/__init__.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +"""Prompt management for CoD Optimal Control workflow using Jinja2 templates.""" + +from pathlib import Path + +from jinja2 import Environment, FileSystemLoader + +PROMPTS_DIR = Path(__file__).parent + + +def get_jinja_env() -> Environment: + """Get Jinja2 environment with template loader.""" + return Environment( + loader=FileSystemLoader(PROMPTS_DIR), + trim_blocks=True, + lstrip_blocks=True, + ) + + +def load_system_prompt( + control_penalty_coef: float = 0.03, + enable_process_noise: bool = False, #!!! + process_noise_std: float = 0.0, #!!! + known_b_sign=None, + **kwargs, +) -> str: + """Load and render the system prompt template. + + Args: + control_penalty_coef: Coefficient used in the control-effort term of + the loss function shown to the agent. + known_b_sign: Disclosed control direction, or ``None`` when hidden. + **kwargs: Additional template variables. + + Returns: + Rendered system prompt string. + """ + env = get_jinja_env() + template = env.get_template("system.jinja2") + return template.render( + control_penalty_coef=control_penalty_coef, + enable_process_noise=enable_process_noise, #!!! + process_noise_std=process_noise_std, #!!! + known_b_sign=known_b_sign, + **kwargs, + ) + + +def load_user_prompt( + x0: float, + v0: float, + x_target: float, + v_target: float, + horizon: int, + **kwargs, +) -> str: + """Load and render the user prompt template for one optimal-control task. + + Args: + x0: Initial position. + v0: Initial velocity. + x_target: Target position. + v_target: Target velocity. + horizon: Rollout horizon T. + **kwargs: Additional template variables. + + Returns: + Rendered user prompt string. + """ + env = get_jinja_env() + template = env.get_template("user.jinja2") + return template.render( + x0=x0, + v0=v0, + x_target=x_target, + v_target=v_target, + horizon=horizon, + **kwargs, + ) + + +__all__ = ["load_system_prompt", "load_user_prompt", "PROMPTS_DIR"] diff --git a/trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/system.jinja2 b/trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/system.jinja2 new file mode 100644 index 00000000000..0e91dc10c97 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/system.jinja2 @@ -0,0 +1,102 @@ +You are solving a one-dimensional optimal-control problem by designing a scalar feedback expression. + +## Environment Dynamics + +The state is `(x_t, v_t)`. At step `t`, the environment evaluates your expression using the current state and applies the resulting scalar control `u_t`. Controls are applied for `t = 0, 1, ..., T - 1`, and the terminal state `(x_T, v_T)` is evaluated after the final transition. + +```text +{% if enable_process_noise %} +v_(t+1) = a_env * v_t + b_env * u_t + epsilon_t +{% else %} +v_(t+1) = a_env * v_t + b_env * u_t +{% endif %} +x_(t+1) = x_t + v_(t+1) +``` + +The numerical values of `a_env` and `b_env` are unknown and hidden from you. +{% if known_b_sign == "positive" %} +You are told that `b_env` is strictly positive: positive `u_t` increases the control contribution to the next velocity. Its magnitude remains unknown. +{% elif known_b_sign == "negative" %} +You are told that `b_env` is strictly negative: positive `u_t` decreases the control contribution to the next velocity. Its magnitude remains unknown. +{% else %} +The control coefficient `b_env` may be positive or negative, so infer its sign from supplied hints or earlier-controller evidence rather than assuming a direction. +{% endif %} +The environment clips every evaluated control to `[-1, 1]`. +{% if enable_process_noise %} + +The velocity transition also includes an unpredictable disturbance: + +```text +epsilon_t ~ Normal(0, {{ process_noise_std }}^2) +``` + +The noise seed and future values are unavailable. Use current-state feedback to correct disturbances that have already affected the trajectory. +{% endif %} + +## Shared Environment Evidence + +Related tasks in the same pack share the hidden `a_env` and `b_env`. Supplied hints and earlier-controller evidence may therefore help infer the shared dynamics. Earlier tasks can have different initial states, targets, and horizons, so reuse their control law only after adapting it to the current task. + +## Objective + +```text +L = (x_T - x_target)^2 + + 2 * (v_T - v_target)^2 + + {{ control_penalty_coef }} * sum_t u_t^2 +R = 1 / (1 + L) +``` + +Maximize `R` by reaching the target position with the requested terminal velocity while avoiding unnecessary control effort. + +## Action Expression + +Submit one scalar expression. The environment reevaluates it at every step with these variables: + +- `x`: current position `x_t` +- `v`: current velocity `v_t` +- `t`: current integer step index +- `x_target`: target position +- `v_target`: target velocity +- `horizon`: rollout horizon `T` +- `remaining_steps`: `max(1, horizon - t)` + +Supported syntax: + +- finite numeric constants, including decimal and scientific notation +- arithmetic operators: `+`, `-`, `*`, `/`, `**` +- comparisons: `<`, `<=`, `>`, `>=`, `==`, `!=` +- Boolean operators: `and`, `or`, `not` +- conditional expression: `value_if_true if condition else value_if_false` +- numeric functions: `abs(value)`, `min(values...)`, `max(values...)`, `pow(base, exponent)`, and `clip(value, lower, upper)` +- XML-safe conditional functions: `ifelse(condition, value_if_true, value_if_false)` with `lt(a,b)`, `le(a,b)`, `gt(a,b)`, `ge(a,b)`, `eq(a,b)`, or `ne(a,b)` + +Normal arithmetic precedence applies. The final result must be one finite numeric scalar. The environment performs the final `[-1, 1]` clipping, so an outer `clip` is optional. Assignment and local variables are unsupported: write `0.3*(x_target-x)` directly, not `dx = x_target-x`. Statements, function definitions, imports, loops, comprehensions, containers, indexing, attributes, and all other function calls are also unsupported. + +A fixed signed-PD expression can be written as: + +```text +0.25 * (x_target - x) + 0.32 * (v_target - v) +``` + +A time-varying expression can be written without XML escaping by using the XML-safe conditional functions: + +```text +0.25 * (x_target - x) + ifelse(lt(t, horizon - 2), 0.5, 1.0) * (v_target - v) +``` + +Use `ifelse` with `lt/le/gt/ge/eq/ne` in the final XML whenever possible. Raw `<` and `<=` are also valid expression operators, but XML requires them to be escaped as `<` and `<=`. PDE equations normally need no escaping because they do not contain comparison operators. + + + + 0.25 * (x_target - x) + ifelse(lt(t, horizon - 2), 0.5, 1.0) * (v_target - v) + + + +## Response Contract + +Reason briefly about the inferred dynamics and feedback law, then end the response with exactly one `...` submission. + +- The final `` block must be unique and must end the response. +- `` must contain only expression text and no nested XML elements. +- Do not output Python functions, assignments, JSON, Markdown code fences, multiple candidate expressions, additional XML actions, or text after ``. +- Do not expose or assume numerical values for hidden dynamics without evidence. Use the disclosed sign of `b_env` when one is provided. diff --git a/trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/user.jinja2 b/trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/user.jinja2 new file mode 100644 index 00000000000..4bdbd9b0397 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/optimalcontrol/prompts/user.jinja2 @@ -0,0 +1,5 @@ +Initial state: x_0 = {{ "%.3f"|format(x0) }}, v_0 = {{ "%.3f"|format(v0) }}. +Target state: x_target = {{ "%.3f"|format(x_target) }}, v_target = {{ "%.3f"|format(v_target) }}. +The rollout has {{ horizon }} steps (t = 0, 1, ..., {{ horizon - 1 }}). + +Think briefly about the feedback law, then end with exactly one `...` submission. diff --git a/trinity/common/workflows/connect_the_dots/optimalcontrol/workflow.py b/trinity/common/workflows/connect_the_dots/optimalcontrol/workflow.py new file mode 100644 index 00000000000..243fc37d11c --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/optimalcontrol/workflow.py @@ -0,0 +1,673 @@ +# -*- coding: utf-8 -*- +"""CoD workflow for the one-dimensional optimal-control deployment demo. + +Each task is a single-turn controller-design problem: the model submits one +scalar action expression, which is evaluated as closed-loop feedback under +hidden linear dynamics for a fixed horizon. + +The workflow inherits from ``AsyncCoDMultiStepWorkflow`` to reuse standard CoD +utilities (system-prompt augmentation/trajectory stripping, ICL-example +stripping, and trajectory formatting). +""" + +from __future__ import annotations + +import asyncio +import datetime +import json +import os +from typing import TYPE_CHECKING, List, Optional, Tuple + +import openai +import torch + +from trinity.common.experience import Experience +from trinity.common.workflows.connect_the_dots.base_workflow import ( + AsyncCoDMultiStepWorkflow, +) +from trinity.common.workflows.connect_the_dots.optimalcontrol.env import ( + OptimalControlEnv, + format_reward, + serialize_action_expression_xml, + validate_action_expression, +) +from trinity.common.workflows.connect_the_dots.optimalcontrol.prompts import ( + load_system_prompt, + load_user_prompt, +) +from trinity.common.workflows.connect_the_dots.utils import parse_xml_answer +from trinity.common.workflows.workflow import Task + +if TYPE_CHECKING: + from trinity.common.models.model import ModelWrapper + + +_PACK_CONTROL_POLICIES: dict[tuple[str, int], dict[str, object]] = {} #!!! +PARSE_FAILURE_REWARD = -0.1 + + +def parse_action_expression_submission(response: str) -> tuple[Optional[str], str]: + """Parse one terminal XML action expression.""" + if "```" in response: + return None, "python_or_markdown_code_is_not_allowed" + payload, parse_error = parse_xml_answer(response) + if payload is None: + return None, parse_error + answer_end = response.rfind("") + len("") + if response[answer_end:].strip(): + return None, "answer_must_end_the_response" + if payload.get("action") != "action_expression": + return None, "expected_action_expression" + try: + return validate_action_expression(payload.get("args")), "" + except ValueError as error: + return None, str(error) + + +class CoDOptimalControlWorkflow(AsyncCoDMultiStepWorkflow): + """Single-turn action-expression workflow for optimal control. + + The model emits one scalar feedback expression; the workflow validates it, + evaluates it under hidden dynamics, and assigns the rollout reward. + """ + + is_async: bool = True + can_reset: bool = True + can_repeat: bool = False + + def __init__( + self, + model: ModelWrapper, + task: Task, + auxiliary_models: Optional[List] = None, + ): + # The sync OpenAI client from the base class is not needed; in + # external mode an async client is created lazily in `_chat`. + super().__init__( + task=task, + model=model, + auxiliary_models=auxiliary_models, + use_openai_client=False, + ) + self.reset(task) + + def reset(self, task: Task): + """Reset task-specific configuration and environment state.""" + super().reset(task) + + self.workflow_args = task.workflow_args if hasattr(task, "workflow_args") else {} + self.trajectory_dump_dir = self.workflow_args.get("trajectory_dump_dir", None) + self.task_idx = int(self.raw_task["task_idx"]) + self.include_previous_control_policy = bool( + self.workflow_args.get("include_previous_control_policy", False) + ) + self.previous_control_policy: Optional[str] = None + self.submitted_control_policy: Optional[str] = None + self.previous_control_policy_evaluation: Optional[dict[str, object]] = None #!!! + self.submitted_control_policy_evaluation: Optional[dict[str, object]] = None #!!! + + task_config = OptimalControlEnv.resolve_task(self.raw_task, self.workflow_args) + self.a_env = task_config["a_env"] + self.b_env = task_config["b_env"] + self.min_abs_b_env = task_config["min_abs_b_env"] #!!! + self.x0 = task_config["x0"] + self.v0 = task_config["v0"] + self.x_target = task_config["x_target"] + self.v_target = task_config["v_target"] + self.horizon = task_config["horizon"] + self.control_penalty_coef = task_config["control_penalty_coef"] + self.enable_process_noise = task_config["enable_process_noise"] #!!! + self.process_noise_std = task_config["process_noise_std"] #!!! + self.task_seed = task_config["task_seed"] #!!! + + self.env = OptimalControlEnv( + a_env=self.a_env, + b_env=self.b_env, + x0=self.x0, + v0=self.v0, + x_target=self.x_target, + v_target=self.v_target, + horizon=self.horizon, + control_penalty_coef=self.control_penalty_coef, + enable_process_noise=self.enable_process_noise, #!!! + process_noise_std=self.process_noise_std, #!!! + task_seed=self.task_seed, #!!! + ) + + # State for the current task + self.memory: List[dict] = [] + + @property + def max_step_num(self) -> int: + """This is a single-turn workflow: one environment step per task.""" + return 1 + + def _pack_policy_key(self) -> tuple[str, int]: + pack_seed = int(self.raw_task.get("pack_seed", self.raw_task.get("seed", 42))) + return str(self.task.batch_id), pack_seed + + def _build_system_prompt(self) -> str: + """Build system prompt, appending CoD hint and token-limit notes.""" + b_lower, b_upper = map(float, self.workflow_args["b_env_range"]) + known_b_sign = "positive" if b_lower > 0.0 else "negative" if b_upper < 0.0 else None + sys_prompt = load_system_prompt( + control_penalty_coef=self.control_penalty_coef, + enable_process_noise=self.enable_process_noise, #!!! + process_noise_std=self.process_noise_std, #!!! + known_b_sign=known_b_sign, + ) + return self._augment_system_prompt(sys_prompt) + + def _build_user_prompt(self) -> str: + """Build the task-specific user prompt.""" + user_prompt = load_user_prompt( + x0=self.x0, + v0=self.v0, + x_target=self.x_target, + v_target=self.v_target, + horizon=self.horizon, + ) + if self.icl_examples: + user_prompt = ( + f"{user_prompt}\n\nHere are some reference examples:\n\n{self.icl_examples}" + ) + if self.previous_control_policy: #!!! + evaluation = self.previous_control_policy_evaluation or {} + executed = bool(evaluation.get("action_execution_success", False)) + reward = float(evaluation.get("reward", 0.0)) + reward_text = format_reward(reward) + position_error = evaluation.get("signed_position_error") + velocity_error = evaluation.get("signed_velocity_error") + behavior = ( + "The expression completed the earlier rollout." + if executed + else "The expression did not complete the earlier rollout." + ) + position_text = "unknown" if position_error is None else f"{float(position_error):.3f}" + velocity_text = "unknown" if velocity_error is None else f"{float(velocity_error):.3f}" + user_prompt += ( #!!! + "\n\n## Controller evidence from an earlier task\n\n" + "The expression and measurements below come from an earlier, different " + "task in the same hidden environment. They are not rollout results for " + "the current task. Use them only as evidence about the shared hidden " + "dynamics and controller behavior.\n\n" + f"- Earlier initial state: x0={evaluation.get('x0')}, " + f"v0={evaluation.get('v0')}\n" + f"- Earlier target: x_target={evaluation.get('x_target')}, " + f"v_target={evaluation.get('v_target')}\n" + f"- Earlier horizon: {evaluation.get('horizon')}\n" + f"- Completed that earlier rollout: {'yes' if executed else 'no'}\n" + f"- Reward on that earlier task: {reward_text}\n" + f"- Signed terminal position residual x_T - x_target: {position_text}\n" + f"- Signed terminal velocity residual v_T - v_target: {velocity_text}\n" + f"- Main behavior: {behavior}\n\n" + "Action expression used on that earlier task:\n\n" + f"{serialize_action_expression_xml(self.previous_control_policy)}\n\n" #!!! + "Adapt the expression to the current initial state, target, and horizon, " + "or submit a new one if it performed poorly." + ) #!!! + return user_prompt + + def _build_feedback( + self, + result: Optional[dict], + evaluation_status: str, + error_detail: Optional[str] = None, + response_truncated: bool = False, + ) -> str: + """Build environment feedback string for this rollout.""" + if evaluation_status == "format_error" and response_truncated: + return ( + "Invalid response: generation reached the maximum response length before a " + "valid final action-expression XML submission was completed. The environment " + f"was not executed and reward was set to {PARSE_FAILURE_REWARD}." + ) + if evaluation_status == "format_error": + detail = error_detail or "invalid action-expression XML" + return ( + "Invalid controller submission: expected exactly one terminal " + "... block " + f"({detail}). The environment was not executed and reward was set to " + f"{PARSE_FAILURE_REWARD}." + ) + if evaluation_status == "action_error": + detail = error_detail or "expression evaluation failed." + return ( + f"Invalid policy: {detail}. Controller evaluation stopped when the expression " + "failed; no complete trajectory or loss was returned, and reward was forced " + "to 0." + ) + if result is None: + return "Invalid policy: controller evaluation failed. Reward was forced to 0." + + return "\n\n".join( + [ + "The environment executed the submitted policy under the fixed hidden dynamics.", + self.env.render_trajectory(result), + self.env.render_summary(result), + ] + ) + + def _build_rollout_trajectory( + self, + system_prompt: str, + user_prompt: str, + response_text: str, + action_expression: Optional[str], + ) -> str: + """Build controller evidence for CoD hint generation.""" + if action_expression is not None: + policy_label = "Submitted action expression" + policy = serialize_action_expression_xml(action_expression) + else: + policy_label = "Raw agent response; no valid action expression was extracted" + policy = response_text + return "\n\n".join( + [ + f"System:\n{self._strip_system_prompt(system_prompt)}", + f"Task:\n{self._strip_icl_examples(user_prompt)}", + f"{policy_label}:\n{policy}", + ] + ) + + def _dump_trajectory( + self, + run_id: int, + system_prompt: str, + user_prompt: str, + response_text: str, + action_expression: Optional[str], + result: Optional[dict], + format_error: bool, + action_error: bool, + response_token_count: int, + max_response_tokens: Optional[int], + response_hit_max_tokens: bool, + response_truncated: bool, + error_detail: Optional[str], + feedback: str, + ) -> None: + """Persist one task trajectory to a JSONL file for offline analysis.""" + if not self.trajectory_dump_dir: + return + + os.makedirs(self.trajectory_dump_dir, exist_ok=True) + dump_path = os.path.join( + self.trajectory_dump_dir, + "trajectories.jsonl", + ) + + # This workflow only handles the actual solve_task step; the CoD + # hint-generation step is executed by AsyncCoDUpdateContextWorkflow and + # already captured in the CoD per-part JSON logs. + exp_type = "solve_task" + rollout = None + metrics = { + "reward": ( + PARSE_FAILURE_REWARD + if format_error + else 0.0 if result is None else result["reward"] + ), + "format_error": format_error, + "expression_schema_error": format_error, + "action_error": action_error, + "response_token_count": response_token_count, + "max_response_tokens": max_response_tokens, + "response_hit_max_tokens": response_hit_max_tokens, + "response_truncated": response_truncated, + } + if result is not None: + rollout = { + "xs": result["xs"], + "vs": result["vs"], + "us": result["us"], + "process_noises": result["process_noises"], + } + metrics.update( + { + "loss": result["loss"], + "x_final": result["x_final"], + "v_final": result["v_final"], + } + ) + + record = { + "timestamp": datetime.datetime.now().isoformat(), + "run_id": run_id, + "batch_id": self.task.batch_id, + "task_id": self.task.task_id, + "task_idx": self.task_idx, + "seed": self.raw_task.get("seed", None), + "exp_type": exp_type, + "a_env": self.a_env, + "b_env": self.b_env, + "min_abs_b_env": self.min_abs_b_env, #!!! + "x0": self.x0, + "v0": self.v0, + "x_target": self.x_target, + "v_target": self.v_target, + "horizon": self.horizon, + "control_penalty_coef": self.control_penalty_coef, + "enable_process_noise": self.enable_process_noise, #!!! + "process_noise_std": self.process_noise_std, #!!! + "task_seed": self.task_seed, #!!! + "system_prompt": system_prompt, + "user_prompt": user_prompt, + "response_text": response_text, + "action_expression": action_expression, + "controller_xml": ( + None + if action_expression is None + else serialize_action_expression_xml(action_expression) + ), + "controller_error_detail": error_detail, + "hint": self.hint, + "rollout": rollout, + "metrics": metrics, + "feedback": feedback, + } + + try: + with open(dump_path, "a", encoding="utf-8") as f: + f.write(json.dumps(record, ensure_ascii=False) + "\n") + except Exception as e: + # Never crash the main workflow because of a logging failure. + print(f"[OptimalControlWorkflow] Failed to dump trajectory: {e}") + + async def _chat(self, messages: List[dict]) -> List[Experience]: + """Generate responses via the local engine or an external API. + + For local engines (vLLM/SGLang) this delegates to ``chat_async``. In + Trinity's native external mode (``model.external_model.enable: true``, + bench only), ``ModelWrapper.model`` is ``None`` and only the OpenAI + client path is available, so the external API is called directly and + each choice is wrapped into a minimal ``Experience``. + """ + if self.model.model is not None: + return await self.model.chat_async(messages, **self.rollout_args) + + # Trinity appends `/v1` when building the OpenAI client, so tolerate + # base URLs that already include the suffix (e.g. DashScope's + # `.../compatible-mode/v1`). + if ( + self.model.openai_async_client is None + and self.model.api_address + and self.model.api_address.rstrip("/").endswith("/v1") + ): + self.model.api_address = self.model.api_address.rstrip("/")[: -len("/v1")] + client = self.model.get_openai_async_client() + rollout_args = self.rollout_args + request_kwargs = { + "model": self.model.config.external_model_config.model_name, + "messages": messages, + "max_completion_tokens": rollout_args.get("max_tokens") + or self.model.config.max_response_tokens, + "n": rollout_args.get("n", 1), + } + if rollout_args.get("temperature") is not None: + request_kwargs["temperature"] = rollout_args["temperature"] + # DashScope-compatible endpoints expect a top-level `enable_thinking` + # in extra_body (not vLLM-style chat_template_kwargs). + if self.model.config.enable_thinking is not None: + request_kwargs["extra_body"] = {"enable_thinking": self.model.config.enable_thinking} + + max_retries = 5 + response = None + for attempt in range(max_retries): + try: + response = await client.chat.completions.create(**request_kwargs) + break + except (openai.RateLimitError, openai.InternalServerError): + if attempt == max_retries - 1: + raise + await asyncio.sleep(1.0 * (2**attempt)) + + usage_metrics = {} + usage = getattr(response, "usage", None) + if usage is not None: + for usage_key in ("prompt_tokens", "completion_tokens", "total_tokens"): + usage_val = getattr(usage, usage_key, None) + if isinstance(usage_val, (int, float)): + usage_metrics[f"usage/{usage_key}"] = float(usage_val) + + experiences: List[Experience] = [] + for choice in response.choices: + finish_reason = str(choice.finish_reason or "").lower() + experiences.append( + Experience( + # Minimal valid token tensor; external APIs return no token ids. + tokens=torch.tensor([0, 0], dtype=torch.int32), + logprobs=torch.tensor([0.0], dtype=torch.float32), + prompt_length=1, + response_text=choice.message.content or "", + truncate_status=("response_truncated" if finish_reason == "length" else None), + metrics=dict(usage_metrics), + info={"finish_reason": finish_reason}, + ) + ) + return experiences + + async def step_async(self, step_num: int) -> Tuple[bool, List[Experience]]: + """Execute one single-turn optimal-control task.""" + system_prompt = self._build_system_prompt() + user_prompt = self._build_user_prompt() + + self.memory = [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_prompt}, + ] + if self.reply_prefix: + self.memory.append({"role": "assistant", "content": self.reply_prefix}) + + responses = await self._chat(self.memory) + + experiences: List[Experience] = [] + for i, response in enumerate(responses): + response.eid.run = i + response.eid.step = 0 + response_text = response.response_text or "" + response.response_text = response_text + action_expression, expression_error = parse_action_expression_submission(response_text) + controller_xml = ( + None + if action_expression is None + else serialize_action_expression_xml(action_expression) + ) + + max_response_tokens = self.rollout_args.get("max_tokens") + if max_response_tokens is None: + max_response_tokens = self.model.config.max_response_tokens + if self.model.model is None: + response_token_count = ( + int(response.metrics.get("usage/completion_tokens", -1)) + if len(responses) == 1 + else -1 + ) + response_hit_max_tokens = response.truncate_status == "response_truncated" + else: + response_token_count = len(response.tokens) - response.prompt_length + response_hit_max_tokens = ( + max_response_tokens is not None and response_token_count >= max_response_tokens + ) + response_truncated = response_hit_max_tokens and action_expression is None + + if action_expression is None: + result = None + evaluation_status = "format_error" + error_detail = expression_error + else: + result, evaluation_status, error_detail = self.env.rollout_action_expression( + action_expression + ) + format_error = evaluation_status == "format_error" + action_error = evaluation_status == "action_error" + reward = ( + PARSE_FAILURE_REWARD + if format_error + else 0.0 if result is None else float(result["reward"]) + ) + feedback = self._build_feedback( + result, + evaluation_status=evaluation_status, + error_detail=error_detail, + response_truncated=response_truncated, + ) + if action_expression is not None: #!!! + self.submitted_control_policy = action_expression #!!! + self.submitted_control_policy_evaluation = { #!!! + "format_parse_success": True, #!!! + "action_execution_success": not action_error, #!!! + "reward": reward, #!!! + "max_score": 1.0, #!!! + "x_final": None if result is None else float(result["x_final"]), #!!! + "v_final": None if result is None else float(result["v_final"]), #!!! + "position_error": ( + None if result is None else abs(float(result["x_final"]) - self.x_target) + ), + "velocity_error": ( + None if result is None else abs(float(result["v_final"]) - self.v_target) + ), + "signed_position_error": ( + None if result is None else float(result["x_final"]) - self.x_target + ), + "signed_velocity_error": ( + None if result is None else float(result["v_final"]) - self.v_target + ), + "x0": self.x0, + "v0": self.v0, + "x_target": self.x_target, + "v_target": self.v_target, + "horizon": self.horizon, + "feedback": feedback, #!!! + } #!!! + trajectory = self._build_rollout_trajectory( + system_prompt, + user_prompt, + response_text, + action_expression, + ) + + response.reward = reward + if response.metrics is None: + response.metrics = {} + response.metrics.update( + { + "reward": reward, + "format_error": float(format_error), + "format_error_termination": float(format_error), + "expression_schema_error": float(format_error), + "action_error": float(action_error), + "response_token_count": float(response_token_count), + "response_hit_max_tokens": float(response_hit_max_tokens), + "response_truncated": float(response_truncated), + "previous_control_policy_used": float(bool(self.previous_control_policy)), + "process_noise_rms": ( #!!! + 0.0 if result is None else float(result["process_noise_rms"]) #!!! + ), #!!! + } + ) + if result is not None: + response.metrics.update( + { + "loss": float(result["loss"]), + "x_final": float(result["x_final"]), + "v_final": float(result["v_final"]), + } + ) + + if response.info is None: + response.info = {} + response.info.update( + { + "sys_prompt": system_prompt, + "user_prompt": user_prompt, + "feedback": feedback, + "trajectory": trajectory, + "a_env": self.a_env, + "b_env": self.b_env, + "min_abs_b_env": self.min_abs_b_env, #!!! + "x0": self.x0, + "v0": self.v0, + "x_target": self.x_target, + "v_target": self.v_target, + "horizon": self.horizon, + "previous_control_policy": self.previous_control_policy or "", + "action_expression": action_expression or "", + "controller_xml": controller_xml or "", + "previous_control_policy_evaluation": ( #!!! + self.previous_control_policy_evaluation or {} #!!! + ), #!!! + "enable_process_noise": self.enable_process_noise, #!!! + "process_noise_std": self.process_noise_std, #!!! + "task_seed": self.task_seed, #!!! + "early_termination_by_format_issue": format_error, + "expression_schema_error": format_error, + "action_error": action_error, + "response_token_count": response_token_count, + "max_response_tokens": max_response_tokens, + "response_hit_max_tokens": response_hit_max_tokens, + "response_truncated": response_truncated, + "controller_error_detail": error_detail or "", + } + ) + + # Persist and update conversation memory. + self._dump_trajectory( + run_id=response.eid.run, + system_prompt=system_prompt, + user_prompt=user_prompt, + response_text=response_text, + action_expression=action_expression, + result=result, + format_error=format_error, + action_error=action_error, + response_token_count=response_token_count, + max_response_tokens=max_response_tokens, + response_hit_max_tokens=response_hit_max_tokens, + response_truncated=response_truncated, + error_detail=error_detail, + feedback=feedback, + ) + + experiences.append(response) + + # Single-step workflow: do not continue after this turn. + return False, experiences + + async def run_async(self) -> List[Experience]: + """Run one optimal-control task with optional pack-level policy reuse.""" + policy_key = None + if self.include_previous_control_policy: + policy_key = self._pack_policy_key() + if self.task_idx == 0: + _PACK_CONTROL_POLICIES.pop(policy_key, None) + previous_record = _PACK_CONTROL_POLICIES.get(policy_key) #!!! + if previous_record: #!!! + self.previous_control_policy = str(previous_record["policy"]) #!!! + self.previous_control_policy_evaluation = dict( #!!! + previous_record.get("evaluation", {}) #!!! + ) #!!! + + _, experiences = await self.step_async(step_num=0) + + if policy_key is not None: + submitted_evaluation = self.submitted_control_policy_evaluation or {} + if self.submitted_control_policy and bool( + submitted_evaluation.get("action_execution_success", False) + ): + _PACK_CONTROL_POLICIES[policy_key] = { #!!! + "policy": self.submitted_control_policy, #!!! + "evaluation": submitted_evaluation, #!!! + } #!!! + pack_size = int(self.raw_task.get("pack_size", self.task_idx + 1)) + if self.task_idx + 1 >= pack_size: + _PACK_CONTROL_POLICIES.pop(policy_key, None) + + if experiences: + # Set step and env-step count only on the last experience; CoD + # post-processing will propagate metrics as needed. The trajectory + # string was already built per-response during step_async. + experiences[-1].eid.step = 0 + experiences[-1].metrics["actual_env_steps"] = 1 + + return experiences diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/__init__.py b/trinity/common/workflows/connect_the_dots/pde_discovery/__init__.py new file mode 100644 index 00000000000..870580d9a35 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +"""CoD workflow for PDE discovery.""" + +from trinity.common.workflows.connect_the_dots.pde_discovery.workflow import ( + CoDPDEDiscoveryWorkflow, +) + +__all__ = ["CoDPDEDiscoveryWorkflow"] diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/candidate.py b/trinity/common/workflows/connect_the_dots/pde_discovery/candidate.py new file mode 100644 index 00000000000..f41b6a0690e --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/candidate.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- +"""Dictionary and candidate-equation utilities for PDE discovery.""" + +from __future__ import annotations + +import re +from typing import Dict, List, Tuple + +import numpy as np + + +DEFAULT_DICTIONARY = [ + "u", + "u**2", + "u**3", + "u**4", + "u**5", + "u**7", + "sin(u)", + "sin(2*u)", + "exp(u)-1", + "log(1+u**2)", + "tanh(u)", + "u/(1+u**2)", + "u**2/(1+u**2)", + "u**4/(1+u**4)", + "u/(1+u+u**2)", +] + +SYMBOLIC_COEFFICIENT_RE = re.compile( + r"(? str: + if term == "u": + return r"u(?!\*\*)" + escaped = re.escape(term) + if "+" in term or "-" in term: + # format_equation wraps compound terms to preserve expression grouping. + return rf"(?:{escaped}|\({escaped}\))" + return escaped + + +def symbolic_coefficient_tokens(equation: object) -> List[str]: + return sorted(set(SYMBOLIC_COEFFICIENT_RE.findall(str(equation or "")))) + + +def basis_values(term: str, u: np.ndarray) -> np.ndarray: + if term == "u": + return u + if term == "u**2": + return u**2 + if term == "u**3": + return u**3 + if term == "u**4": + return u**4 + if term == "u**5": + return u**5 + if term == "u**7": + return u**7 + if term == "sin(u)": + return np.sin(u) + if term == "sin(2*u)": + return np.sin(2.0 * u) + if term == "exp(u)-1": + return np.expm1(u) + if term == "log(1+u**2)": + return np.log1p(u**2) + if term == "tanh(u)": + return np.tanh(u) + if term == "u/(1+u**2)": + return u / (1.0 + u**2) + if term == "u**2/(1+u**2)": + return (u**2) / (1.0 + u**2) + if term == "u**4/(1+u**4)": + return (u**4) / (1.0 + u**4) + if term == "u/(1+u+u**2)": + return u / (1.0 + u + u**2) + raise ValueError(f"Unsupported dictionary term: {term}") + + +def dictionary_matrix( + u: np.ndarray, + dictionary: List[str], +) -> Tuple[np.ndarray, List[str]]: + columns = [] + terms = [] + for term in dictionary: + try: + basis = basis_values(str(term), u) + except ValueError: + continue + if np.all(np.isfinite(basis)): + columns.append(basis.astype(float)) + terms.append(str(term)) + if not columns: + return np.zeros((len(u), 0), dtype=float), [] + return np.column_stack(columns), terms + + +def sanitize_dictionary(dictionary: object, fallback_terms: List[str]) -> List[str]: + if not isinstance(dictionary, list) or not dictionary: + return list(fallback_terms) + sanitized = [] + for term in dictionary: + term = str(term) + if term in DEFAULT_DICTIONARY and term not in sanitized: + sanitized.append(term) + return sanitized or list(fallback_terms) + + +def reaction_value(u: np.ndarray, coefficients: Dict[str, float]) -> np.ndarray: + values = np.zeros_like(u, dtype=float) + for term, coef in coefficients.items(): + try: + values = values + coef * basis_values(term, u) + except ValueError: + continue + return values + + +def format_equation(coefficients: Dict[str, float]) -> str: + if not coefficients: + return "0" + + def format_term(term: str) -> str: + if "+" in term or "-" in term: + return f"({term})" + return term + + pieces = [ + f"{coef:.3g}*{format_term(term)}" + for term, coef in coefficients.items() + ] + return pieces[0] + "".join( + f" {'+' if piece[0] != '-' else '-'} {piece.lstrip('-')}" + for piece in pieces[1:] + ) + + +def candidate_coefficients( + candidate: str, + dictionary_terms: List[str], +) -> Dict[str, float]: + text = str(candidate or "").strip() + if "=" in text: + left, right = text.split("=", 1) + # Accept only the equation form used by the environment. + if not re.fullmatch(r"\s*f\s*\(\s*u\s*\)\s*", left, re.IGNORECASE): + return {} + text = right + + coefficients: Dict[str, float] = {} + remaining = re.sub(r"\s+", "", text) + for term in sorted(dictionary_terms, key=len, reverse=True): + pattern = ( + r"([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)" + r"\*" + + term_pattern(term) + ) + while match := re.search(pattern, remaining, flags=re.IGNORECASE): + # Sum repeated terms rather than silently keeping only one. + coefficients[term] = coefficients.get(term, 0.0) + float(match.group(1)) + start, end = match.span() + remaining = remaining[:start] + (" " * (end - start)) + remaining[end:] + + # Reject unsupported terms instead of silently dropping them. + if remaining.replace(" ", "") not in {"", "+", "-"}: + return {} + return { + term: coefficient + for term, coefficient in coefficients.items() + if abs(coefficient) > 1e-12 + } + + +def candidate_support( + candidate: str, + dictionary: List[str], + dictionary_terms: List[str], +) -> List[str]: + coefficient_support = candidate_coefficients(candidate, dictionary_terms) + if coefficient_support: + return [term for term in dictionary if term in coefficient_support] + compact = candidate.replace(" ", "") + support = [] + for term in sorted(dictionary, key=len, reverse=True): + pattern = term_pattern(term) + if re.search(pattern, compact, flags=re.IGNORECASE): + support.append(term) + compact = re.sub(pattern, "", compact, flags=re.IGNORECASE) + support_set = set(support) + return [term for term in dictionary if term in support_set] diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth.py b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth.py new file mode 100644 index 00000000000..6d8bb0fc9f1 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth.py @@ -0,0 +1,286 @@ +# -*- coding: utf-8 -*- +"""Hidden ground-truth sampling for PDE discovery.""" + +from __future__ import annotations + +import json +import os +import re +from functools import lru_cache +from itertools import product +from typing import Dict, List, Tuple + +import numpy as np + +from .candidate import ( + DEFAULT_DICTIONARY, + reaction_value, +) +from . import pde_numeric + + +DEFAULT_GT_FAMILY_DIR = os.path.join( + os.path.dirname(__file__), + "ground_truth_families", +) +DEFAULT_HIDDEN_GT_FAMILY_FILE = "physical_general.json" +COEFFICIENT_ABS_MIN = 0.5 +COEFFICIENT_ABS_MAX = 2.0 +GT_STABILITY_MAX_ATTEMPTS = 128 +GT_STABILITY_GRID_SIZE = 65 +GT_STABILITY_TIME_STEPS = 401 + + +def _serialized_templates(templates: List[dict]) -> tuple: + return tuple( + ( + tuple(template["terms"]), + tuple( + (term, float(template.get("signs", {}).get(term, 1.0))) + for term in template["terms"] + ), + ) + for template in templates + ) + + +@lru_cache(maxsize=None) +def _calibrated_family_initial_amplitude_upper( + serialized_templates: tuple, + min_terms: int, + max_terms: int, + state_abs_limit: float, +) -> float: + """Find one conservative amplitude bound shared by a GT family.""" + nx = GT_STABILITY_GRID_SIZE + nt = GT_STABILITY_TIME_STEPS + x_grid = np.linspace(0.0, 1.0, nx) + dt = 1.0 / (nt - 1) + dx = 1.0 / (nx - 1) + ratio = dt / (dx * dx) + interior = nx - 2 + lower = -ratio * np.ones(interior - 1, dtype=float) + diag = (1.0 + 2.0 * ratio) * np.ones(interior, dtype=float) + upper = -ratio * np.ones(interior - 1, dtype=float) + + # Cover the corners of the allowed coefficient box for every eligible support. + coefficient_vertices = [] + for terms, raw_signs in serialized_templates: + if not min_terms <= len(terms) <= max_terms: + continue + signs = dict(raw_signs) + for magnitudes in product( + (COEFFICIENT_ABS_MIN, COEFFICIENT_ABS_MAX), + repeat=len(terms), + ): + coefficient_vertices.append( + { + term: (1.0 if signs[term] >= 0.0 else -1.0) * magnitude + for term, magnitude in zip(terms, magnitudes) + } + ) + if not coefficient_vertices: + raise ValueError("Ground-truth family has no templates to calibrate") + + state_limit = float(state_abs_limit) + if not np.isfinite(state_limit) or state_limit <= 0.0: + raise ValueError("state_abs_limit must be finite and positive") + # The first Dirichlet mode is the broadest profile and receives the least + # diffusion damping among the admissible sine modes. + base_profile = np.sin(np.pi * x_grid) + + def family_is_stable(amplitude: float) -> bool: + for coefficients in coefficient_vertices: + state = amplitude * base_profile + for _ in range(nt - 1): + with np.errstate(over="ignore", invalid="ignore", divide="ignore"): + rhs = state[1:-1] + dt * reaction_value( + state[1:-1], coefficients + ) + if not np.all(np.isfinite(rhs)): + return False + next_state = pde_numeric.solve_tridiagonal( + lower, diag, upper, rhs + ) + if ( + not np.all(np.isfinite(next_state)) + or np.any(np.abs(next_state) >= state_limit) + ): + return False + state[1:-1] = next_state + return True + + safe = 0.0 + unsafe = state_limit + resolution = state_limit / (GT_STABILITY_GRID_SIZE - 1) + while unsafe - safe > resolution: + midpoint = (safe + unsafe) / 2.0 + if family_is_stable(midpoint): + safe = midpoint + else: + unsafe = midpoint + if safe <= 0.0: + raise ValueError("Ground-truth family has no positive stable amplitude") + return safe + + +def calibrate_family_initial_amplitude_upper( + templates: List[dict], + min_terms: int, + max_terms: int, + state_abs_limit: float, +) -> float: + """Return a cached, ground-truth-independent bound for one family.""" + return _calibrated_family_initial_amplitude_upper( + _serialized_templates(templates), + int(min_terms), + int(max_terms), + float(state_abs_limit), + ) + + +def load_hidden_gt_templates( + dictionary_terms: List[str], + family: str = DEFAULT_HIDDEN_GT_FAMILY_FILE, +) -> List[dict]: + family_file = str(family).strip() + if family_file.endswith(".json"): + family_file = family_file[:-5] + if not re.fullmatch(r"[A-Za-z0-9_-]+", family_file): + raise ValueError(f"Invalid hidden ground-truth family: {family!r}") + path = os.path.join(DEFAULT_GT_FAMILY_DIR, f"{family_file}.json") + with open(path, "r", encoding="utf-8") as file: + payload = json.load(file) + raw_templates = payload.get("templates", payload) if isinstance(payload, dict) else payload + if not isinstance(raw_templates, list): + raise ValueError(f"Hidden GT family file has no template list: {path}") + + templates = [] + for raw_template in raw_templates: + if not isinstance(raw_template, dict): + continue + terms = [ + str(term) + for term in raw_template.get("terms", []) + if str(term) in DEFAULT_DICTIONARY and str(term) in dictionary_terms + ] + terms = list(dict.fromkeys(terms)) + if not terms: + continue + raw_signs = raw_template.get("signs", {}) + signs = {} + if isinstance(raw_signs, dict): + for term in terms: + try: + sign = float(raw_signs.get(term, 0.0)) + except (TypeError, ValueError): + sign = 0.0 + signs[term] = 1.0 if sign >= 0.0 else -1.0 + templates.append({"terms": terms, "signs": signs}) + if not templates: + raise ValueError(f"Hidden GT family file has no usable templates: {path}") + return templates + + +def sample_hidden_reaction( + rng: np.random.Generator, + templates: List[dict], + min_terms: int, + max_terms: int, + template_index: int | None = None, +) -> Tuple[List[str], Dict[str, float]]: + eligible = [ + template + for template in templates + if min_terms <= len(template["terms"]) <= max_terms + ] + if not eligible: + raise ValueError( + "Hidden GT family has no templates compatible with " + f"min_reaction_terms={min_terms}, max_reaction_terms={max_terms}." + ) + if template_index is None: + template = eligible[int(rng.integers(0, len(eligible)))] + else: + if not 0 <= template_index < len(eligible): + raise ValueError( + f"ground_truth_template_index={template_index} is outside the " + f"eligible template range [0, {len(eligible) - 1}]" + ) + template = eligible[template_index] + terms = sorted(list(template["terms"]), key=DEFAULT_DICTIONARY.index) + signs = template.get("signs", {}) + coefficients = {} + for term in terms: + sign = float(signs.get(term, 0.0)) + if abs(sign) <= 1e-12: + sign = 1.0 if rng.random() < 0.5 else -1.0 + magnitude = round( + float(rng.uniform(COEFFICIENT_ABS_MIN, COEFFICIENT_ABS_MAX)), + 2, + ) + coefficients[term] = (1.0 if sign >= 0.0 else -1.0) * magnitude + return terms, coefficients + + +def is_stable_reaction_candidate( + coefficients: Dict[str, float], + pde_grid_size: int, + pde_time_steps: int, + trajectory_count: int, + pack_seed: int, + initial_amplitude_upper: float, + initial_condition_shape: pde_numeric.InitialConditionShapeConfig, + state_abs_limit: float, +) -> bool: + nx = max(17, min(pde_grid_size, GT_STABILITY_GRID_SIZE)) + nt = max(17, min(pde_time_steps, GT_STABILITY_TIME_STEPS)) + x_grid = np.linspace(0.0, 1.0, nx) + t_grid = np.linspace(0.0, 1.0, nt) + dx = float(x_grid[1] - x_grid[0]) + dt = float(t_grid[1] - t_grid[0]) + r = dt / (dx * dx) + + interior = nx - 2 + lower = -r * np.ones(interior - 1, dtype=float) + diag = (1.0 + 2.0 * r) * np.ones(interior, dtype=float) + upper = -r * np.ones(interior - 1, dtype=float) + + clip_limit = float(state_abs_limit) + if not np.isfinite(clip_limit) or clip_limit <= 0.0: + raise ValueError("state_abs_limit must be finite and positive") + for traj_idx in range(trajectory_count): + u_grid = np.zeros((nt, nx), dtype=float) + u_grid[0] = pde_numeric.initial_condition( + x_grid, + pack_seed=pack_seed, + trajectory_index=traj_idx, + trajectory_count=trajectory_count, + amplitude_upper=initial_amplitude_upper, + shape_config=initial_condition_shape, + ) + u_grid[:, 0] = 0.0 + u_grid[:, -1] = 0.0 + if not np.all(np.isfinite(u_grid[0])): + return False + + for step in range(nt - 1): + rhs_raw = u_grid[step, 1:-1] + dt * reaction_value( + u_grid[step, 1:-1], + coefficients, + ) + if ( + not np.all(np.isfinite(rhs_raw)) + or np.any(rhs_raw <= -clip_limit) + or np.any(rhs_raw >= clip_limit) + ): + return False + u_next = pde_numeric.solve_tridiagonal(lower, diag, upper, rhs_raw) + if ( + not np.all(np.isfinite(u_next)) + or np.any(u_next <= -clip_limit) + or np.any(u_next >= clip_limit) + ): + return False + u_grid[step + 1, 1:-1] = u_next + return True diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full.json b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full.json new file mode 100644 index 00000000000..4b272744b00 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full.json @@ -0,0 +1,61 @@ +{ + "name": "physical_full", + "description": "Expanded physical PDE reaction family with one- and two-term dissipative, periodic-potential, and biochemical kinetics. The agent does not see this file.", + "templates": [ + {"terms": ["u"], "signs": {"u": -1}}, + {"terms": ["u**3"], "signs": {"u**3": -1}}, + {"terms": ["sin(u)"], "signs": {"sin(u)": -1}}, + {"terms": ["tanh(u)"], "signs": {"tanh(u)": -1}}, + {"terms": ["u**2/(1+u**2)"], "signs": {"u**2/(1+u**2)": -1}}, + {"terms": ["u", "u**2"], "signs": {"u": 1, "u**2": -1}}, + {"terms": ["u", "u**3"], "signs": {"u": 1, "u**3": -1}}, + {"terms": ["u", "u**4"], "signs": {"u": 1, "u**4": -1}}, + {"terms": ["u", "u**5"], "signs": {"u": 1, "u**5": -1}}, + {"terms": ["u**2", "u**3"], "signs": {"u**2": 1, "u**3": -1}}, + {"terms": ["u**2", "u**4"], "signs": {"u**2": 1, "u**4": -1}}, + {"terms": ["sin(u)", "u**3"], "signs": {"sin(u)": 1, "u**3": -1}}, + {"terms": ["sin(u)", "u**5"], "signs": {"sin(u)": 1, "u**5": -1}}, + {"terms": ["exp(u)-1", "u**2"], "signs": {"exp(u)-1": 1, "u**2": -1}}, + {"terms": ["exp(u)-1", "u**3"], "signs": {"exp(u)-1": 1, "u**3": -1}}, + {"terms": ["log(1+u**2)", "u**2"], "signs": {"log(1+u**2)": 1, "u**2": -1}}, + {"terms": ["log(1+u**2)", "u**4"], "signs": {"log(1+u**2)": 1, "u**4": -1}}, + {"terms": ["tanh(u)", "u**2"], "signs": {"tanh(u)": 1, "u**2": -1}}, + {"terms": ["tanh(u)", "u**3"], "signs": {"tanh(u)": 1, "u**3": -1}}, + {"terms": ["tanh(u)", "u**5"], "signs": {"tanh(u)": 1, "u**5": -1}}, + {"terms": ["u/(1+u**2)", "u**2"], "signs": {"u/(1+u**2)": 1, "u**2": -1}}, + {"terms": ["u/(1+u**2)", "u**3"], "signs": {"u/(1+u**2)": 1, "u**3": -1}}, + {"terms": ["u/(1+u**2)", "u**5"], "signs": {"u/(1+u**2)": 1, "u**5": -1}}, + {"terms": ["u**2/(1+u**2)", "u"], "signs": {"u**2/(1+u**2)": 1, "u": -1}}, + {"terms": ["u**2/(1+u**2)", "u**3"], "signs": {"u**2/(1+u**2)": 1, "u**3": -1}}, + {"terms": ["u**2/(1+u**2)", "u**4"], "signs": {"u**2/(1+u**2)": 1, "u**4": -1}}, + {"family": "phase_field", "terms": ["u**3", "u**5"], "signs": {"u**3": 1, "u**5": -1}}, + {"family": "phase_field", "terms": ["u", "u**7"], "signs": {"u": 1, "u**7": -1}}, + {"family": "mean_field_relaxation", "terms": ["u", "tanh(u)"], "signs": {"u": -1, "tanh(u)": 1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": 1, "sin(2*u)": -1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": 1, "sin(2*u)": 1}}, + {"family": "hill_kinetics", "terms": ["u**2/(1+u**2)", "u**4/(1+u**4)"], "signs": {"u**2/(1+u**2)": 1, "u**4/(1+u**4)": -1}}, + {"family": "hill_kinetics", "terms": ["u**2/(1+u**2)", "u**4/(1+u**4)"], "signs": {"u**2/(1+u**2)": -1, "u**4/(1+u**4)": 1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": -1, "sin(2*u)": -1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": -1, "sin(2*u)": 1}}, + {"family": "hill_kinetics", "terms": ["u", "u**4/(1+u**4)"], "signs": {"u": -1, "u**4/(1+u**4)": 1}}, + {"family": "haldane_kinetics", "terms": ["u", "u/(1+u+u**2)"], "signs": {"u": -1, "u/(1+u+u**2)": 1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**2"], "signs": {"sin(2*u)": 1, "u**2": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**3"], "signs": {"sin(2*u)": 1, "u**3": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**5"], "signs": {"sin(2*u)": 1, "u**5": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(u)", "u**4"], "signs": {"sin(u)": 1, "u**4": -1}}, + {"family": "bounded_activation", "terms": ["tanh(u)", "u**4"], "signs": {"tanh(u)": 1, "u**4": -1}}, + {"family": "substrate_inhibition", "terms": ["u/(1+u**2)", "u**4"], "signs": {"u/(1+u**2)": 1, "u**4": -1}}, + {"family": "haldane_kinetics", "terms": ["u/(1+u+u**2)", "u**2"], "signs": {"u/(1+u+u**2)": 1, "u**2": -1}}, + {"family": "haldane_kinetics", "terms": ["u/(1+u+u**2)", "u**3"], "signs": {"u/(1+u+u**2)": 1, "u**3": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**2"], "signs": {"sin(2*u)": -1, "u**2": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**3"], "signs": {"sin(2*u)": -1, "u**3": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**5"], "signs": {"sin(2*u)": -1, "u**5": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(u)", "u**4"], "signs": {"sin(u)": -1, "u**4": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(u)", "u**3"], "signs": {"sin(u)": -1, "u**3": -1}}, + {"family": "substrate_inhibition", "terms": ["u", "u/(1+u**2)"], "signs": {"u": -1, "u/(1+u**2)": 1}}, + {"family": "hill_kinetics", "terms": ["u**2", "u**2/(1+u**2)"], "signs": {"u**2": -1, "u**2/(1+u**2)": 1}}, + {"family": "higher_order_phase_field", "terms": ["u**3", "u**7"], "signs": {"u**3": 1, "u**7": -1}}, + {"family": "higher_order_population", "terms": ["u**2", "u**5"], "signs": {"u**2": 1, "u**5": -1}}, + {"family": "hill_kinetics", "terms": ["u**2/(1+u**2)", "u**5"], "signs": {"u**2/(1+u**2)": 1, "u**5": -1}} + ] +} diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval.json b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval.json new file mode 100644 index 00000000000..59671315966 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval.json @@ -0,0 +1,21 @@ +{ + "name": "physical_full_eval", + "description": "Support-disjoint evaluation split of the expanded physical PDE family. Its basis terms are seen during training, but its two-term compositions are held out. The agent does not see this file.", + "templates": [ + {"terms": ["u", "u**4"], "signs": {"u": 1, "u**4": -1}}, + {"terms": ["u**2", "u**3"], "signs": {"u**2": 1, "u**3": -1}}, + {"terms": ["sin(u)", "u**5"], "signs": {"sin(u)": 1, "u**5": -1}}, + {"terms": ["exp(u)-1", "u**3"], "signs": {"exp(u)-1": 1, "u**3": -1}}, + {"terms": ["log(1+u**2)", "u**4"], "signs": {"log(1+u**2)": 1, "u**4": -1}}, + {"terms": ["tanh(u)", "u**5"], "signs": {"tanh(u)": 1, "u**5": -1}}, + {"terms": ["u/(1+u**2)", "u**5"], "signs": {"u/(1+u**2)": 1, "u**5": -1}}, + {"terms": ["u**2/(1+u**2)", "u**4"], "signs": {"u**2/(1+u**2)": 1, "u**4": -1}}, + {"family": "phase_field", "terms": ["u", "u**7"], "signs": {"u": 1, "u**7": -1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": 1, "sin(2*u)": -1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": 1, "sin(2*u)": 1}}, + {"family": "hill_kinetics", "terms": ["u**2/(1+u**2)", "u**4/(1+u**4)"], "signs": {"u**2/(1+u**2)": 1, "u**4/(1+u**4)": -1}}, + {"family": "hill_kinetics", "terms": ["u**2/(1+u**2)", "u**4/(1+u**4)"], "signs": {"u**2/(1+u**2)": -1, "u**4/(1+u**4)": 1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": -1, "sin(2*u)": -1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": -1, "sin(2*u)": 1}} + ] +} diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval_4000.json b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval_4000.json new file mode 100644 index 00000000000..c05edee720f --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval_4000.json @@ -0,0 +1,31 @@ +{ + "name": "physical_full_eval_4000", + "description": "Support-disjoint 4000-task evaluation family with 17 two-term and 8 three-term synthetic reaction kinetics. Every basis term is seen during training, while every complete support is held out. The added templates are physically motivated phenomenological combinations for the benchmark's calibrated non-negative, bounded state range; they are not claims of unique canonical equations. The agent does not see this file.", + "templates": [ + {"terms": ["u", "u**4"], "signs": {"u": 1, "u**4": -1}}, + {"terms": ["u**2", "u**3"], "signs": {"u**2": 1, "u**3": -1}}, + {"terms": ["sin(u)", "u**5"], "signs": {"sin(u)": 1, "u**5": -1}}, + {"terms": ["exp(u)-1", "u**3"], "signs": {"exp(u)-1": 1, "u**3": -1}}, + {"terms": ["log(1+u**2)", "u**4"], "signs": {"log(1+u**2)": 1, "u**4": -1}}, + {"terms": ["tanh(u)", "u**5"], "signs": {"tanh(u)": 1, "u**5": -1}}, + {"terms": ["u/(1+u**2)", "u**5"], "signs": {"u/(1+u**2)": 1, "u**5": -1}}, + {"terms": ["u**2/(1+u**2)", "u**4"], "signs": {"u**2/(1+u**2)": 1, "u**4": -1}}, + {"family": "phase_field", "terms": ["u", "u**7"], "signs": {"u": 1, "u**7": -1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": 1, "sin(2*u)": -1}}, + {"family": "hill_kinetics", "terms": ["u**2/(1+u**2)", "u**4/(1+u**4)"], "signs": {"u**2/(1+u**2)": 1, "u**4/(1+u**4)": -1}}, + {"family": "exponential_absorption", "terms": ["exp(u)-1", "u**5"], "signs": {"exp(u)-1": 1, "u**5": -1}}, + {"family": "exponential_periodic_competition", "terms": ["exp(u)-1", "sin(u)"], "signs": {"exp(u)-1": -1, "sin(u)": 1}}, + {"family": "higher_order_phase_field", "terms": ["u**5", "u**7"], "signs": {"u**5": 1, "u**7": -1}}, + {"family": "bounded_saturating_competition", "terms": ["tanh(u)", "u/(1+u**2)"], "signs": {"tanh(u)": -1, "u/(1+u**2)": 1}}, + {"family": "adjacent_polynomial_competition", "terms": ["u**3", "u**4"], "signs": {"u**3": 1, "u**4": -1}}, + {"family": "even_saturating_competition", "terms": ["log(1+u**2)", "u**2/(1+u**2)"], "signs": {"log(1+u**2)": -1, "u**2/(1+u**2)": 1}}, + {"family": "polynomial_logistic", "terms": ["u", "u**2", "u**3"], "signs": {"u": 1, "u**2": 1, "u**3": -1}}, + {"family": "bounded_polynomial", "terms": ["u", "tanh(u)", "u**3"], "signs": {"u": 1, "tanh(u)": 1, "u**3": -1}}, + {"family": "phase_field", "terms": ["u", "u**3", "u**5"], "signs": {"u": 1, "u**3": -1, "u**5": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(u)", "sin(2*u)", "u**3"], "signs": {"sin(u)": 1, "sin(2*u)": -1, "u**3": -1}}, + {"family": "higher_order_phase_field", "terms": ["u", "u**3", "u**7"], "signs": {"u": 1, "u**3": 1, "u**7": -1}}, + {"family": "saturating_high_order_kinetics", "terms": ["u**2/(1+u**2)", "u**4/(1+u**4)", "u**5"], "signs": {"u**2/(1+u**2)": 1, "u**4/(1+u**4)": 1, "u**5": -1}}, + {"family": "exponential_bounded_high_order", "terms": ["exp(u)-1", "tanh(u)", "u**7"], "signs": {"exp(u)-1": 1, "tanh(u)": 1, "u**7": -1}}, + {"family": "periodic_saturating_high_order", "terms": ["sin(2*u)", "u**2/(1+u**2)", "u**7"], "signs": {"sin(2*u)": 1, "u**2/(1+u**2)": 1, "u**7": -1}} + ] +} diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval_hard.json b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval_hard.json new file mode 100644 index 00000000000..973d5c072ed --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_eval_hard.json @@ -0,0 +1,24 @@ +{ + "name": "physical_full_eval_hard", + "description": "Support-disjoint evaluation split with 12 two-term and 6 three-term PDEs spanning simple polynomial, periodic, high-order, and saturating kinetics. Its basis terms are seen during training, but its compositions are held out. The agent does not see this file.", + "templates": [ + {"terms": ["u", "u**4"], "signs": {"u": 1, "u**4": -1}}, + {"terms": ["u**2", "u**3"], "signs": {"u**2": 1, "u**3": -1}}, + {"terms": ["sin(u)", "u**5"], "signs": {"sin(u)": 1, "u**5": -1}}, + {"terms": ["exp(u)-1", "u**3"], "signs": {"exp(u)-1": 1, "u**3": -1}}, + {"terms": ["log(1+u**2)", "u**4"], "signs": {"log(1+u**2)": 1, "u**4": -1}}, + {"terms": ["tanh(u)", "u**5"], "signs": {"tanh(u)": 1, "u**5": -1}}, + {"terms": ["u/(1+u**2)", "u**5"], "signs": {"u/(1+u**2)": 1, "u**5": -1}}, + {"terms": ["u**2/(1+u**2)", "u**4"], "signs": {"u**2/(1+u**2)": 1, "u**4": -1}}, + {"family": "phase_field", "terms": ["u", "u**7"], "signs": {"u": 1, "u**7": -1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": 1, "sin(2*u)": -1}}, + {"family": "hill_kinetics", "terms": ["u**2/(1+u**2)", "u**4/(1+u**4)"], "signs": {"u**2/(1+u**2)": 1, "u**4/(1+u**4)": -1}}, + {"family": "exponential_absorption", "terms": ["exp(u)-1", "u**5"], "signs": {"exp(u)-1": 1, "u**5": -1}}, + {"family": "polynomial_logistic", "terms": ["u", "u**2", "u**3"], "signs": {"u": 1, "u**2": 1, "u**3": -1}}, + {"family": "bounded_polynomial", "terms": ["u", "tanh(u)", "u**3"], "signs": {"u": 1, "tanh(u)": 1, "u**3": -1}}, + {"family": "phase_field", "terms": ["u", "u**3", "u**5"], "signs": {"u": 1, "u**3": -1, "u**5": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(u)", "sin(2*u)", "u**3"], "signs": {"sin(u)": 1, "sin(2*u)": -1, "u**3": -1}}, + {"family": "higher_order_phase_field", "terms": ["u", "u**3", "u**7"], "signs": {"u": 1, "u**3": 1, "u**7": -1}}, + {"family": "saturating_high_order_kinetics", "terms": ["u**2/(1+u**2)", "u**4/(1+u**4)", "u**5"], "signs": {"u**2/(1+u**2)": 1, "u**4/(1+u**4)": 1, "u**5": -1}} + ] +} diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_train.json b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_train.json new file mode 100644 index 00000000000..b85470d4e70 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_full_train.json @@ -0,0 +1,46 @@ +{ + "name": "physical_full_train", + "description": "Training split of the expanded physical PDE family. It covers every dictionary basis with one- and two-term reactions. The agent does not see this file.", + "templates": [ + {"terms": ["u"], "signs": {"u": -1}}, + {"terms": ["u**3"], "signs": {"u**3": -1}}, + {"terms": ["sin(u)"], "signs": {"sin(u)": -1}}, + {"terms": ["tanh(u)"], "signs": {"tanh(u)": -1}}, + {"terms": ["u**2/(1+u**2)"], "signs": {"u**2/(1+u**2)": -1}}, + {"terms": ["u", "u**2"], "signs": {"u": 1, "u**2": -1}}, + {"terms": ["u", "u**3"], "signs": {"u": 1, "u**3": -1}}, + {"terms": ["u", "u**5"], "signs": {"u": 1, "u**5": -1}}, + {"terms": ["u**2", "u**4"], "signs": {"u**2": 1, "u**4": -1}}, + {"terms": ["sin(u)", "u**3"], "signs": {"sin(u)": 1, "u**3": -1}}, + {"terms": ["exp(u)-1", "u**2"], "signs": {"exp(u)-1": 1, "u**2": -1}}, + {"terms": ["log(1+u**2)", "u**2"], "signs": {"log(1+u**2)": 1, "u**2": -1}}, + {"terms": ["tanh(u)", "u**2"], "signs": {"tanh(u)": 1, "u**2": -1}}, + {"terms": ["tanh(u)", "u**3"], "signs": {"tanh(u)": 1, "u**3": -1}}, + {"terms": ["u/(1+u**2)", "u**2"], "signs": {"u/(1+u**2)": 1, "u**2": -1}}, + {"terms": ["u/(1+u**2)", "u**3"], "signs": {"u/(1+u**2)": 1, "u**3": -1}}, + {"terms": ["u**2/(1+u**2)", "u"], "signs": {"u**2/(1+u**2)": 1, "u": -1}}, + {"terms": ["u**2/(1+u**2)", "u**3"], "signs": {"u**2/(1+u**2)": 1, "u**3": -1}}, + {"family": "phase_field", "terms": ["u**3", "u**5"], "signs": {"u**3": 1, "u**5": -1}}, + {"family": "mean_field_relaxation", "terms": ["u", "tanh(u)"], "signs": {"u": -1, "tanh(u)": 1}}, + {"family": "hill_kinetics", "terms": ["u", "u**4/(1+u**4)"], "signs": {"u": -1, "u**4/(1+u**4)": 1}}, + {"family": "haldane_kinetics", "terms": ["u", "u/(1+u+u**2)"], "signs": {"u": -1, "u/(1+u+u**2)": 1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**2"], "signs": {"sin(2*u)": 1, "u**2": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**3"], "signs": {"sin(2*u)": 1, "u**3": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**5"], "signs": {"sin(2*u)": 1, "u**5": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(u)", "u**4"], "signs": {"sin(u)": 1, "u**4": -1}}, + {"family": "bounded_activation", "terms": ["tanh(u)", "u**4"], "signs": {"tanh(u)": 1, "u**4": -1}}, + {"family": "substrate_inhibition", "terms": ["u/(1+u**2)", "u**4"], "signs": {"u/(1+u**2)": 1, "u**4": -1}}, + {"family": "haldane_kinetics", "terms": ["u/(1+u+u**2)", "u**2"], "signs": {"u/(1+u+u**2)": 1, "u**2": -1}}, + {"family": "haldane_kinetics", "terms": ["u/(1+u+u**2)", "u**3"], "signs": {"u/(1+u+u**2)": 1, "u**3": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**2"], "signs": {"sin(2*u)": -1, "u**2": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**3"], "signs": {"sin(2*u)": -1, "u**3": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(2*u)", "u**5"], "signs": {"sin(2*u)": -1, "u**5": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(u)", "u**4"], "signs": {"sin(u)": -1, "u**4": -1}}, + {"family": "periodic_potential_with_absorption", "terms": ["sin(u)", "u**3"], "signs": {"sin(u)": -1, "u**3": -1}}, + {"family": "substrate_inhibition", "terms": ["u", "u/(1+u**2)"], "signs": {"u": -1, "u/(1+u**2)": 1}}, + {"family": "hill_kinetics", "terms": ["u**2", "u**2/(1+u**2)"], "signs": {"u**2": -1, "u**2/(1+u**2)": 1}}, + {"family": "higher_order_phase_field", "terms": ["u**3", "u**7"], "signs": {"u**3": 1, "u**7": -1}}, + {"family": "higher_order_population", "terms": ["u**2", "u**5"], "signs": {"u**2": 1, "u**5": -1}}, + {"family": "hill_kinetics", "terms": ["u**2/(1+u**2)", "u**5"], "signs": {"u**2/(1+u**2)": 1, "u**5": -1}} + ] +} diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_general.json b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_general.json new file mode 100644 index 00000000000..65785c588f4 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_general.json @@ -0,0 +1,38 @@ +{ + "name": "physical_general", + "description": "A broader hidden PDE reaction template family over the full nonlinear dictionary. The agent does not see this file.", + "templates": [ + {"terms": ["u"], "signs": {"u": -1}}, + {"terms": ["u**2"], "signs": {"u**2": -1}}, + {"terms": ["u**3"], "signs": {"u**3": -1}}, + {"terms": ["u**4"], "signs": {"u**4": -1}}, + {"terms": ["u**5"], "signs": {"u**5": -1}}, + {"terms": ["sin(u)"], "signs": {"sin(u)": -1}}, + {"terms": ["exp(u)-1"], "signs": {"exp(u)-1": -1}}, + {"terms": ["log(1+u**2)"], "signs": {"log(1+u**2)": -1}}, + {"terms": ["tanh(u)"], "signs": {"tanh(u)": -1}}, + {"terms": ["u/(1+u**2)"], "signs": {"u/(1+u**2)": -1}}, + {"terms": ["u**2/(1+u**2)"], "signs": {"u**2/(1+u**2)": -1}}, + {"terms": ["u", "u**2"], "signs": {"u": 1, "u**2": -1}}, + {"terms": ["u", "u**3"], "signs": {"u": 1, "u**3": -1}}, + {"terms": ["u", "u**4"], "signs": {"u": 1, "u**4": -1}}, + {"terms": ["u", "u**5"], "signs": {"u": 1, "u**5": -1}}, + {"terms": ["u**2", "u**3"], "signs": {"u**2": 1, "u**3": -1}}, + {"terms": ["u**2", "u**4"], "signs": {"u**2": 1, "u**4": -1}}, + {"terms": ["sin(u)", "u**3"], "signs": {"sin(u)": 1, "u**3": -1}}, + {"terms": ["sin(u)", "u**5"], "signs": {"sin(u)": 1, "u**5": -1}}, + {"terms": ["exp(u)-1", "u**2"], "signs": {"exp(u)-1": 1, "u**2": -1}}, + {"terms": ["exp(u)-1", "u**3"], "signs": {"exp(u)-1": 1, "u**3": -1}}, + {"terms": ["log(1+u**2)", "u**2"], "signs": {"log(1+u**2)": 1, "u**2": -1}}, + {"terms": ["log(1+u**2)", "u**4"], "signs": {"log(1+u**2)": 1, "u**4": -1}}, + {"terms": ["tanh(u)", "u**2"], "signs": {"tanh(u)": 1, "u**2": -1}}, + {"terms": ["tanh(u)", "u**3"], "signs": {"tanh(u)": 1, "u**3": -1}}, + {"terms": ["tanh(u)", "u**5"], "signs": {"tanh(u)": 1, "u**5": -1}}, + {"terms": ["u/(1+u**2)", "u**2"], "signs": {"u/(1+u**2)": 1, "u**2": -1}}, + {"terms": ["u/(1+u**2)", "u**3"], "signs": {"u/(1+u**2)": 1, "u**3": -1}}, + {"terms": ["u/(1+u**2)", "u**5"], "signs": {"u/(1+u**2)": 1, "u**5": -1}}, + {"terms": ["u**2/(1+u**2)", "u"], "signs": {"u**2/(1+u**2)": 1, "u": -1}}, + {"terms": ["u**2/(1+u**2)", "u**3"], "signs": {"u**2/(1+u**2)": 1, "u**3": -1}}, + {"terms": ["u**2/(1+u**2)", "u**4"], "signs": {"u**2/(1+u**2)": 1, "u**4": -1}} + ] +} diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_hard.json b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_hard.json new file mode 100644 index 00000000000..e39d18e6a8b --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_hard.json @@ -0,0 +1,13 @@ +{ + "name": "physical_hard", + "description": "Hard two-term hidden PDE reactions drawn from phase-field, population, periodic-potential, and saturating biochemical kinetics. The agent does not see this file.", + "templates": [ + {"family": "phase_field", "terms": ["u", "u**3"], "signs": {"u": 1, "u**3": -1}}, + {"family": "population", "terms": ["u", "u**2"], "signs": {"u": 1, "u**2": -1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": -1, "sin(2*u)": -1}}, + {"family": "periodic_potential", "terms": ["sin(u)", "sin(2*u)"], "signs": {"sin(u)": -1, "sin(2*u)": 1}}, + {"family": "hill_kinetics", "terms": ["u", "u**2/(1+u**2)"], "signs": {"u": -1, "u**2/(1+u**2)": 1}}, + {"family": "hill_kinetics", "terms": ["u", "u**4/(1+u**4)"], "signs": {"u": -1, "u**4/(1+u**4)": 1}}, + {"family": "haldane_kinetics", "terms": ["u", "u/(1+u+u**2)"], "signs": {"u": -1, "u/(1+u+u**2)": 1}} + ] +} diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_simplified.json b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_simplified.json new file mode 100644 index 00000000000..4c002045254 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/ground_truth_families/physical_simplified.json @@ -0,0 +1,23 @@ +{ + "name": "physical_simplified", + "description": "Hidden PDE reaction templates for the simplified physical benchmark. The agent does not see this file.", + "templates": [ + {"terms": ["u"], "signs": {"u": -1}}, + {"terms": ["u**3"], "signs": {"u**3": -1}}, + {"terms": ["u**5"], "signs": {"u**5": -1}}, + {"terms": ["tanh(u)"], "signs": {"tanh(u)": -1}}, + {"terms": ["u/(1+u**2)"], "signs": {"u/(1+u**2)": -1}}, + {"terms": ["log(1+u**2)"], "signs": {"log(1+u**2)": -1}}, + {"terms": ["u**2/(1+u**2)"], "signs": {"u**2/(1+u**2)": -1}}, + {"terms": ["u", "u**2"], "signs": {"u": 1, "u**2": -1}}, + {"terms": ["u", "u**3"], "signs": {"u": 1, "u**3": -1}}, + {"terms": ["u", "u**5"], "signs": {"u": 1, "u**5": -1}}, + {"terms": ["u**2", "u**3"], "signs": {"u**2": 1, "u**3": -1}}, + {"terms": ["sin(u)", "u**3"], "signs": {"sin(u)": 1, "u**3": -1}}, + {"terms": ["tanh(u)", "u**3"], "signs": {"tanh(u)": 1, "u**3": -1}}, + {"terms": ["u/(1+u**2)", "u**3"], "signs": {"u/(1+u**2)": 1, "u**3": -1}}, + {"terms": ["tanh(u)", "u**2"], "signs": {"tanh(u)": 1, "u**2": -1}}, + {"terms": ["u/(1+u**2)", "u**2"], "signs": {"u/(1+u**2)": 1, "u**2": -1}}, + {"terms": ["log(1+u**2)", "u**2"], "signs": {"log(1+u**2)": 1, "u**2": -1}} + ] +} diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/pde_numeric.py b/trinity/common/workflows/connect_the_dots/pde_discovery/pde_numeric.py new file mode 100644 index 00000000000..367c5bec28e --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/pde_numeric.py @@ -0,0 +1,293 @@ +# -*- coding: utf-8 -*- +"""Numerical helpers for the PDE discovery workflow.""" + +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from typing import Callable + +import numpy as np + + +GROUND_TRUTH_STREAM = 0 +INITIAL_AMPLITUDE_STREAM = 1 +INITIAL_SHAPE_STREAM = 2 + + +@dataclass(frozen=True) +class InitialConditionShapeConfig: + """Configurable complexity prior for random smooth initial conditions.""" + + mode_count_range: tuple[int, int] + + def __post_init__(self) -> None: + value = self.mode_count_range + if len(value) != 2 or value[0] < 1 or value[1] < value[0]: + raise ValueError("mode_count_range must be a positive [min, max] pair") + + @classmethod + def from_mapping( + cls, + raw: Mapping[str, object] | None, + ) -> "InitialConditionShapeConfig": + if raw is None: + raise ValueError("initial_condition_shape configuration is required") + allowed = {"mode_count_range"} + unknown = set(raw) - allowed + if unknown: + raise ValueError( + "Unsupported initial_condition_shape keys: " + + ", ".join(sorted(unknown)) + ) + missing = allowed - set(raw) + if missing: + raise ValueError( + "Missing initial_condition_shape keys: " + + ", ".join(sorted(missing)) + ) + + def count_range(name: str) -> tuple[int, int]: + value = raw[name] + if ( + not isinstance(value, Sequence) + or isinstance(value, (str, bytes)) + or len(value) != 2 + ): + raise ValueError(f"{name} must be a two-element list") + return int(value[0]), int(value[1]) + + return cls(mode_count_range=count_range("mode_count_range")) + + +def initial_condition_amplitudes( + pack_seed: int, + trajectory_count: int, + amplitude_upper: float, +) -> list[float]: + """Sample one amplitude from each stratum below a calibrated upper bound.""" + count = max(1, int(trajectory_count)) + upper = float(amplitude_upper) + if not np.isfinite(upper) or upper <= 0.0: + raise ValueError("initial amplitude upper bound must be finite and positive") + band_edges = np.linspace(0.0, upper, count + 1) + rng = np.random.default_rng( + np.random.SeedSequence([int(pack_seed), INITIAL_AMPLITUDE_STREAM]) + ) + amplitudes = [ + float(rng.uniform(band_edges[index], band_edges[index + 1])) + for index in range(count) + ] + rng.shuffle(amplitudes) + return amplitudes + + +def _normalized_modes( + x_grid: np.ndarray, + rng: np.random.Generator, + mode_count: int, +) -> np.ndarray: + domain_length = float(x_grid[-1] - x_grid[0]) + if domain_length <= 0.0: + raise ValueError("initial-condition grid must span a positive domain") + coordinate = (x_grid - x_grid[0]) / domain_length + modes = np.zeros_like(x_grid, dtype=float) + coefficients = rng.normal(size=mode_count) + for mode, coefficient in enumerate(coefficients, start=1): + modes += coefficient * np.sin(mode * np.pi * coordinate) + scale = float(np.max(np.abs(modes))) + return modes / scale if scale > 1e-12 else modes + + +def _random_initial_condition( + x_grid: np.ndarray, + rng: np.random.Generator, + amplitude: float, + config: InitialConditionShapeConfig, +) -> np.ndarray: + domain_length = float(x_grid[-1] - x_grid[0]) + if domain_length <= 0.0: + raise ValueError("initial-condition grid must span a positive domain") + envelope = np.sin(np.pi * (x_grid - x_grid[0]) / domain_length) + mode_count = int( + rng.integers( + config.mode_count_range[0], + config.mode_count_range[1] + 1, + ) + ) + modes = _normalized_modes(x_grid, rng, mode_count) + shape = envelope * (modes - float(np.min(modes))) + shape_scale = max(float(np.max(shape)), 1e-12) + u0 = amplitude * shape / shape_scale + u0[0] = 0.0 + u0[-1] = 0.0 + return u0 + + +def initial_condition( + x_grid: np.ndarray, + pack_seed: int, + trajectory_index: int = 0, + trajectory_count: int = 1, + amplitude_upper: float | None = None, + shape_config: InitialConditionShapeConfig | None = None, +) -> np.ndarray: + seed_sequence = np.random.SeedSequence( + [int(pack_seed), INITIAL_SHAPE_STREAM, int(trajectory_index)] + ) + rng = np.random.default_rng(seed_sequence) + if amplitude_upper is None: + raise ValueError("initial conditions require a calibrated amplitude upper bound") + amplitudes = initial_condition_amplitudes( + pack_seed, + trajectory_count, + amplitude_upper=amplitude_upper, + ) + if not 0 <= trajectory_index < len(amplitudes): + raise ValueError( + f"trajectory_index={trajectory_index} is outside " + f"trajectory_count={trajectory_count}" + ) + if shape_config is None: + raise ValueError("initial_condition_shape configuration is required") + return _random_initial_condition( + x_grid, + rng, + amplitudes[trajectory_index], + shape_config, + ) + + +def solve_tridiagonal( + lower: np.ndarray, + diag: np.ndarray, + upper: np.ndarray, + rhs: np.ndarray, +) -> np.ndarray: + n = len(diag) + c_prime = np.zeros(max(0, n - 1), dtype=float) + d_prime = np.zeros(n, dtype=float) + denom = diag[0] + if n > 1: + c_prime[0] = upper[0] / denom + d_prime[0] = rhs[0] / denom + for i in range(1, n): + denom = diag[i] - lower[i - 1] * c_prime[i - 1] + if i < n - 1: + c_prime[i] = upper[i] / denom + d_prime[i] = (rhs[i] - lower[i - 1] * d_prime[i - 1]) / denom + solution = np.zeros(n, dtype=float) + solution[-1] = d_prime[-1] + for i in range(n - 2, -1, -1): + solution[i] = d_prime[i] - c_prime[i] * solution[i + 1] + return solution + + +def second_derivative_x(values: np.ndarray, dx: float) -> np.ndarray: + deriv2 = np.zeros_like(values) + deriv2[:, 1:-1] = ( + values[:, 2:] - 2.0 * values[:, 1:-1] + values[:, :-2] + ) / (dx * dx) + deriv2[:, 0] = deriv2[:, 1] + deriv2[:, -1] = deriv2[:, -2] + return deriv2 + + +def scheme_consistent_reaction_residual( + u_grid: np.ndarray, + dx: float, + dt: float, +) -> np.ndarray: + y_grid = np.zeros_like(u_grid, dtype=float) + if len(u_grid) < 2: + return y_grid + u_xx_next = second_derivative_x(u_grid[1:], dx) + y_grid[:-1, 1:-1] = ( + (u_grid[1:, 1:-1] - u_grid[:-1, 1:-1]) / dt + - u_xx_next[:, 1:-1] + ) + return y_grid + + +def simulate_dense_trajectory( + x_grid: np.ndarray, + t_grid: np.ndarray, + dx: float, + dt: float, + lower: np.ndarray, + diag: np.ndarray, + upper: np.ndarray, + trajectory_index: int, + reaction_fn: Callable[[np.ndarray], np.ndarray], + pack_seed: int, + trajectory_count: int = 1, + initial_amplitude_upper: float | None = None, + initial_condition_shape: InitialConditionShapeConfig | None = None, + state_abs_limit: float | None = None, +) -> dict: + if state_abs_limit is None or not np.isfinite(state_abs_limit) or state_abs_limit <= 0.0: + raise ValueError("state_abs_limit must be finite and positive") + nx = len(x_grid) + nt = len(t_grid) + u_grid = np.zeros((nt, nx), dtype=float) + u_grid[0] = initial_condition( + x_grid, + pack_seed, + trajectory_index, + trajectory_count=trajectory_count, + amplitude_upper=initial_amplitude_upper, + shape_config=initial_condition_shape, + ) + u_grid[:, 0] = 0.0 + u_grid[:, -1] = 0.0 + + for step in range(nt - 1): + rhs = u_grid[step, 1:-1] + dt * reaction_fn(u_grid[step, 1:-1]) + rhs = np.clip(rhs, -state_abs_limit, state_abs_limit) + u_next = solve_tridiagonal(lower, diag, upper, rhs) + u_grid[step + 1, 1:-1] = np.clip( + u_next, + -state_abs_limit, + state_abs_limit, + ) + + reaction_residual = scheme_consistent_reaction_residual(u_grid, dx, dt) + # The final state has no forward step, so evaluate f(u) directly. + reaction_residual[-1, 1:-1] = reaction_fn(u_grid[-1, 1:-1]) + return { + "x_grid": x_grid, + "t_grid": t_grid, + "u": u_grid, + "y": reaction_residual, + } + + +def interpolate_dense_field( + field: dict, + field_name: str, + x: np.ndarray, + t: np.ndarray, +) -> np.ndarray: + x_grid = field["x_grid"] + t_grid = field["t_grid"] + values = field[field_name] + + xi = np.searchsorted(x_grid, x, side="right") - 1 + ti = np.searchsorted(t_grid, t, side="right") - 1 + xi = np.clip(xi, 0, len(x_grid) - 2) + ti = np.clip(ti, 0, len(t_grid) - 2) + x0 = x_grid[xi] + x1 = x_grid[xi + 1] + t0 = t_grid[ti] + t1 = t_grid[ti + 1] + wx = (x - x0) / np.maximum(x1 - x0, 1e-12) + wt = (t - t0) / np.maximum(t1 - t0, 1e-12) + + v00 = values[ti, xi] + v01 = values[ti, xi + 1] + v10 = values[ti + 1, xi] + v11 = values[ti + 1, xi + 1] + return ( + (1.0 - wt) * ((1.0 - wx) * v00 + wx * v01) + + wt * ((1.0 - wx) * v10 + wx * v11) + ) diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/prompts/__init__.py b/trinity/common/workflows/connect_the_dots/pde_discovery/prompts/__init__.py new file mode 100644 index 00000000000..4c8bae2838d --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/prompts/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +"""Prompt management for CoD PDE discovery.""" + +from pathlib import Path + +from jinja2 import Environment, FileSystemLoader + +PROMPTS_DIR = Path(__file__).parent + + +def get_jinja_env() -> Environment: + return Environment( + loader=FileSystemLoader(PROMPTS_DIR), + trim_blocks=True, + lstrip_blocks=True, + ) + + +def load_system_prompt(**kwargs) -> str: + env = get_jinja_env() + template = env.get_template("system.jinja2") + return template.render(**kwargs) + + +def load_user_prompt(**kwargs) -> str: + env = get_jinja_env() + template = env.get_template("user.jinja2") + return template.render(**kwargs) diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/prompts/system.jinja2 b/trinity/common/workflows/connect_the_dots/pde_discovery/prompts/system.jinja2 new file mode 100644 index 00000000000..83dd5351f04 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/prompts/system.jinja2 @@ -0,0 +1,127 @@ +You are a scientific discovery agent. Your goal is to identify the hidden nonlinear reaction term f(u) in + + partial_t u = partial_xx u + f(u) + +## Scientific setting + +Every task in one pack uses the same hidden reaction term f(u) but observes a different simulated trajectory generated from a different initial condition. Sampled datasets remain available until the pack ends. + +At a sampled point, u_sampled is the observed state and y_sampled is the noisy reaction target that f(u_sampled) should predict. + +## Candidate function library + +Model the unknown reaction as a sparse linear combination f(u) = sum_k c_k phi_k(u). The dictionary below lists the candidate basis functions phi_k(u): + +{{ dictionary_terms | join(", ") }} + +An equation's support is the subset of dictionary terms with nonzero coefficients. The hidden equation has {{ support_size_hint }} active terms; their identities and coefficients are not revealed. + +## Available actions + +The examples below illustrate XML syntax only. Their concrete values are not recommendations; choose arguments appropriate to the current task instead of copying the examples. + +### sample_pde_data + +Choose spatial-temporal coordinate pairs at which to sample the current trajectory. + + + + + + + + + + +point_grid is required. Each point is one pair (x, t) satisfying 0 < x < 1 and 0 <= t <= 1, and the number of points must fit the remaining budget. The action stores the measurements and returns their dataset_id plus coverage summaries; use that id in run_sparse_regression. + +### summarize_pack_evidence + +Inspect hypotheses recorded in previous tasks from the same pack. + + + + false + + + +reveal_equations is optional and defaults to false. Set it to true to include earlier agent-recorded equations, which are hypotheses rather than ground truth. + +### run_sparse_regression + +Fit and compare sparse equations on sampled data using one of two input modes: + +- dictionary: provide a non-empty subset of the candidate library. Regression enumerates and fits supports formed from these terms up to the stated support-size limit. +- candidate_equations: provide one to eight explicit equations with supports to compare. Scoring is restricted to those supports, and every support's coefficients are refitted on the selected data. + +Dictionary mode example: + + + + merged_all + 0.05 + 0.05 + + u + u**2 + u**3 + + + + +Candidate-equation mode example: + + + + merged_all + + 1.2*u - 0.8*u**3 + 0.9*sin(u) - 0.7*u**3 + + + + +- dataset_id: optional; defaults to merged_all, which combines all datasets stored so far in the pack. You may instead use a dataset_id returned by sample_pde_data. +- alpha: optional ridge strength for coefficient screening; larger values can improve stability under noise or correlated terms but shrink coefficients more. Default: 0.05. +- threshold: optional minimum absolute coefficient retained during support selection; larger values favor sparser equations, while smaller values retain weaker terms. Default: 0.05. + +The action returns the best fitted equation, the closest scored equation with different support, and sampled-data fit and conditioning diagnostics. It does not evaluate candidates on hidden ground truth. + +### update_scientific_context + +Finalize the current task and record a tested equation for later tasks in the pack. + + + + 1.2*u - 0.8*u**3 + The cubic support best matches the sampled evidence. + + sin(u) + + + + +preferred_equation is required and must match an equation scored by the latest run_sparse_regression call; copy the returned equation without changing or re-rounding its coefficients. note is an optional short reason for the choice, and uncertain_terms is an optional list of up to four plausible but unresolved dictionary terms. These annotations do not alter preferred_equation. The action is accepted only after sampling and regression in the current task, and an accepted update finalizes the task. + +## Equation syntax + +Use explicit numeric coefficients, not placeholders such as c1, a, or alpha. Treat each dictionary entry as one complete basis function and parenthesize a term containing an internal plus or minus sign: write 0.7*(exp(u)-1), not 0.7*exp(u)-1. + +## Sampled-data feedback + +Regression feedback may include fit summaries and these values for each point in the selected dataset: + + (x, t, u_sampled, y_sampled, f_pred(u_sampled), abs_error) + +Here f_pred is the candidate prediction, abs_error = abs(y_sampled - f_pred), and sampled-data max error is the largest abs_error. Measurements may be noisy. Selected-support kappa and full-dictionary kappa are condition numbers; larger values mean stronger collinearity and weaker numerical distinguishability. The condition penalty is the non-negative log-scale excess of selected-support kappa over the configured threshold. + +The closest different-support candidate is the alternative support with the lowest sampled-data max error. Its relative margin is (alternative_error - best_error) / best_error: a value near zero means the sampled observations poorly distinguish the supports, while a larger positive value means clearer separation. All diagnostics use sampled observations only. + +## Response protocol + +Regardless of how much reasoning you write, every response must end with exactly +one ... block wrapping exactly one action from the list above. +Keep any reasoning before the block brief. Do not stop after the reasoning: if +your analysis is unfinished or the budget is nearly exhausted, still emit your +best available action inside rather than ending without one. Never write +text after the closing , and never output more than one block. diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/prompts/user.jinja2 b/trinity/common/workflows/connect_the_dots/pde_discovery/prompts/user.jinja2 new file mode 100644 index 00000000000..6def6029cd7 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/prompts/user.jinja2 @@ -0,0 +1,53 @@ +Task {{ task_position }}/{{ pack_size }} | Step {{ current_step }}/{{ max_steps }} +Point budget: {{ budget_remaining }}/{{ point_budget }} remaining + +{% if task_description %} +Task: {{ task_description }} +{% endif %} + +{% if available_datasets %} +Datasets: {{ available_datasets | join(", ") }} +{% else %} +Datasets: none +{% endif %} +{% if last_sampled_max_error is not none %} +Latest sampled-data max error: {{ "%.6g" | format(last_sampled_max_error) }} +{% else %} +Latest sampled-data max error: none +{% endif %} + +{% if evidence_memory %} +Prior task evidence: +- task_evidence_status: {{ evidence_memory.get("task_evidence_status", "unknown") }} +{% set observation_summary = evidence_memory.get("observation_summary", {}) %} +{% if observation_summary.get("n") is not none %} +- sampled_points: {{ observation_summary.get("n", 0) }} +{% endif %} +{% if observation_summary.get("u_span") is not none %} +- sampled_u_span: {{ "%.6g" | format(observation_summary.get("u_span", 0.0)) }} +{% endif %} +{% if observation_summary.get("preferred_sampled_max_error") is not none %} +- preferred_sampled_max_error: {{ "%.6g" | format(observation_summary.get("preferred_sampled_max_error", 0.0)) }} +{% endif %} +{% if observation_summary.get("relative_margin") is not none %} +- relative_margin: {{ "%.6g" | format(observation_summary.get("relative_margin", 0.0)) }} +{% endif %} +{% endif %} + +{% if action_feedback %} +Action feedback: +{{ action_feedback }} +{% else %} +No action has been taken in this task yet. +{% endif %} + +Choose the next action. + +{% if budget_remaining > 0 %} +Sampling is available. sample_pde_data must use point_grid coordinates and cannot request more than {{ budget_remaining }} points. +{% else %} +No sampling budget remains. If regression has not been run after the latest samples, run_sparse_regression next; otherwise use update_scientific_context or inspect prior evidence. +{% endif %} +run_sparse_regression may be called whenever you have enough sampled evidence. update_scientific_context requires current-task sampling and at least one current-task run_sparse_regression call. You may decide whether to sample more points, run another regression, or finalize based on the available evidence. + +Include exactly one XML action wrapped in tags. diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/regression.py b/trinity/common/workflows/connect_the_dots/pde_discovery/regression.py new file mode 100644 index 00000000000..69ef5651186 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/regression.py @@ -0,0 +1,417 @@ +# -*- coding: utf-8 -*- +"""Sparse-regression helpers for PDE discovery. + +This module follows the SINDy-style sparse-identification pattern: build a +candidate-library matrix, select sparse supports, then refit coefficients on +the selected terms. This style of sparse regression is commonly used for +data-driven governing-equation and PDE discovery. + +References: Brunton, Steven L., Joshua L. Proctor, and J. Nathan Kutz. +"Discovering governing equations from data: Sparse identification of nonlinear +dynamical systems." arXiv preprint arXiv:1509.03580 (SINDy); Rudy, Samuel H., +et al. "Data-driven discovery of partial differential equations." arXiv +preprint arXiv:1609.06401 (PDE-FIND). +""" + +from __future__ import annotations + +import math +from itertools import combinations +from typing import List, Optional, Tuple + +import numpy as np + +from . import candidate as candidate_utils + + +KAPPA_CAP = 1e12 +DEBIASED_REFIT_ALPHA = 1e-8 + + +def max_abs_error(y_true: np.ndarray, y_pred: np.ndarray) -> float: + if len(y_true) == 0: + return 1.0 + return float(np.max(np.abs(y_true - y_pred))) + + +def relative_error_margin(second_best_error: float, best_error: float) -> float: + gap = max(0.0, float(second_best_error) - float(best_error)) + if best_error > 0.0: + return float(gap / float(best_error)) + return float("inf") if gap > 0.0 else 0.0 + + +def effective_kappa(u: np.ndarray, dictionary: List[str]) -> float: + theta, _ = candidate_utils.dictionary_matrix(u, dictionary) + if theta.shape[1] == 0: + return KAPPA_CAP + scales = np.linalg.norm(theta, axis=0) + valid = scales > 1e-12 + if np.count_nonzero(valid) == 0: + return KAPPA_CAP + theta_norm = theta[:, valid] / scales[valid] + try: + singular_values = np.linalg.svd(theta_norm, compute_uv=False) + except np.linalg.LinAlgError: + return KAPPA_CAP + if len(singular_values) == 0: + return KAPPA_CAP + sigma_min = float(singular_values[-1]) + sigma_max = float(singular_values[0]) + if sigma_min <= 1e-12: + return KAPPA_CAP + return min(float(sigma_max / sigma_min), KAPPA_CAP) + + +def sparse_regression_result( + u: np.ndarray, + y: np.ndarray, + u_objective: np.ndarray, + y_objective: np.ndarray, + dictionary: list, + alpha: float, + threshold: float, + max_reaction_terms: int, + default_blind_penalty: float, + kappa_threshold: float, + allowed_supports: Optional[List[List[str]]] = None, +) -> dict: + theta, terms = candidate_utils.dictionary_matrix(u, dictionary) + if theta.shape[1] == 0: + target_y = y_objective if len(y_objective) else y + max_error = max_abs_error(target_y, np.zeros_like(target_y, dtype=float)) + penalty = max_error + float(default_blind_penalty) + return { + "support": [], + "coefficients": {}, + "equation": "0", + "max_error": max_error, + "condition_ratio": 1.0, + "kappa": KAPPA_CAP, + "penalty": penalty, + "reward_penalty": penalty, + "relative_margin": 0.0, + "second_best_max_error": float("nan"), + "second_best_equation": "not evaluated: no valid dictionary terms", + "diagnostics": {}, + "candidate_diagnostics": [], + "feedback": "Sparse regression failed: no valid dictionary terms.", + } + + dictionary_kappa = effective_kappa(u, terms) + scales = np.linalg.norm(theta, axis=0) + valid = scales > 1e-12 + theta_valid = theta[:, valid] + terms_valid = [term for term, keep in zip(terms, valid) if keep] + scales_valid = scales[valid] + theta_scaled = theta_valid / scales_valid + theta_objective_valid, _ = candidate_utils.dictionary_matrix(u_objective, terms_valid) + active = np.ones(theta_scaled.shape[1], dtype=bool) + coef_scaled_full = np.zeros(theta_scaled.shape[1], dtype=float) + ridge = max(alpha, 0.0) + + for _ in range(8): + if not np.any(active): + break + design = theta_scaled[:, active] + gram = design.T @ design + ridge * np.eye(design.shape[1]) + rhs = design.T @ y + try: + coef_active = np.linalg.solve(gram, rhs) + except np.linalg.LinAlgError: + coef_active = np.linalg.lstsq(gram, rhs, rcond=None)[0] + coef_unscaled_active = coef_active / scales_valid[active] + keep_active = np.abs(coef_unscaled_active) >= threshold + coef_scaled_full[:] = 0.0 + coef_scaled_full[np.where(active)[0]] = coef_active + if np.all(keep_active): + break + active_indices = np.where(active)[0] + active[active_indices[~keep_active]] = False + + hard_cap_terms = max(1, min(max_reaction_terms, len(terms_valid))) + coefficient_threshold = max(float(threshold), 0.0) + + def objective( + max_error_value: float, + support_terms: List[str], + ) -> Tuple[float, float, float]: + support_kappa = effective_kappa(u, support_terms) if support_terms else dictionary_kappa + condition_ratio = max( + 0.0, math.log10(support_kappa) - math.log10(kappa_threshold) + ) + return ( + max_error_value, + support_kappa, + condition_ratio, + ) + + def fit_support(indices: List[int]) -> dict: + support_indices = sorted(set(indices)) + coef_unscaled = np.zeros(theta_scaled.shape[1], dtype=float) + + if support_indices: + design = theta_valid[:, support_indices] + rhs = design.T @ y + try: + if DEBIASED_REFIT_ALPHA > 0.0: + gram = design.T @ design + DEBIASED_REFIT_ALPHA * np.eye( + design.shape[1] + ) + coef_selected = np.linalg.solve(gram, rhs) + else: + coef_selected = np.linalg.lstsq(design, y, rcond=None)[0] + except np.linalg.LinAlgError: + coef_selected = np.linalg.lstsq(design, y, rcond=None)[0] + + kept_pairs = [ + (idx, float(coef)) + for idx, coef in zip(support_indices, coef_selected) + if abs(float(coef)) >= coefficient_threshold + ] + kept_indices = [idx for idx, _ in kept_pairs] + if kept_indices != support_indices: + return fit_support(kept_indices) + for idx, coef in kept_pairs: + coef_unscaled[idx] = coef + + coefficients = { + terms_valid[idx]: round(float(coef_unscaled[idx]), 3) + for idx in support_indices + } + # Score the same rounded equation exposed to the agent. + equation = candidate_utils.format_equation(coefficients) + coefficients = candidate_utils.candidate_coefficients(equation, terms_valid) + coef_unscaled = np.array( + [coefficients.get(term, 0.0) for term in terms_valid], dtype=float + ) + pred_objective = theta_objective_valid @ coef_unscaled + max_error_value = max_abs_error(y_objective, pred_objective) + score, support_kappa, condition_ratio = objective( + max_error_value, + list(coefficients), + ) + return { + "indices": support_indices, + "support": list(coefficients), + "coefficients": coefficients, + "coef_unscaled": coef_unscaled, + "max_error": max_error_value, + "condition_ratio": condition_ratio, + "kappa": support_kappa, + "penalty": score, + } + + initial_indices = [ + int(idx) + for idx, keep in enumerate(active) + if keep and abs(float(coef_scaled_full[idx] / scales_valid[idx])) >= coefficient_threshold + ] + if len(initial_indices) > hard_cap_terms: + seed_coefficients = coef_scaled_full / scales_valid + initial_indices = sorted( + initial_indices, + key=lambda idx: abs(float(seed_coefficients[idx])), + reverse=True, + )[:hard_cap_terms] + + fit_cache = {} + + def cached_fit(indices: List[int]) -> dict: + key = tuple(sorted(set(indices))) + if key not in fit_cache: + fit_cache[key] = fit_support(list(key)) + return fit_cache[key] + + candidate_supports = set() + if allowed_supports is not None: + term_to_index = {term: idx for idx, term in enumerate(terms_valid)} + for support_terms in allowed_supports: + support_indices = [ + term_to_index[term] for term in support_terms if term in term_to_index + ] + candidate_supports.add(tuple(sorted(set(support_indices)))) + else: + if initial_indices: + candidate_supports.add(tuple(sorted(initial_indices))) + for target_size in range(1, hard_cap_terms + 1): + for support_indices in combinations(range(len(terms_valid)), target_size): + candidate_supports.add(tuple(support_indices)) + if not candidate_supports: + candidate_supports.add(tuple()) + + support_candidates = [ + cached_fit(list(candidate_support)) + for candidate_support in sorted(candidate_supports) + ] + + def add_relative_margin(candidate: dict) -> dict: + candidate = dict(candidate) + support_key = tuple(candidate["support"]) + alternatives = [ + other + for other in support_candidates + if tuple(other["support"]) != support_key + ] + if alternatives: + second_best = min(alternatives, key=lambda item: item["max_error"]) + second_best_max_error = float(second_best["max_error"]) + second_best_equation = candidate_utils.format_equation( + second_best["coefficients"] + ) + relative_margin = relative_error_margin( + second_best_max_error, + float(candidate["max_error"]), + ) + else: + second_best_max_error = float("nan") + second_best_equation = "not evaluated: no alternative support supplied" + relative_margin = 0.0 + candidate.update( + { + "relative_margin": float(relative_margin), + "second_best_max_error": second_best_max_error, + "second_best_equation": second_best_equation, + "reward_penalty": float(candidate["max_error"]), + "diagnostics": { + "condition_ratio": float(candidate["condition_ratio"]), + "relative_margin": float(relative_margin), + "second_best_max_error": second_best_max_error, + "second_best_equation": second_best_equation, + }, + "penalty": float(candidate["max_error"]), + } + ) + return candidate + + scored_candidates = [add_relative_margin(candidate) for candidate in support_candidates] + scored_candidates.sort(key=lambda item: item["penalty"]) + best = scored_candidates[0] + equation = candidate_utils.format_equation(best["coefficients"]) + selection_scope = ( + "agent-specified refit set" + if allowed_supports is not None + else "agent-specified dictionary" + ) + selection_note = ( + f" Scored {len(support_candidates)} support(s) from the {selection_scope}." + ) + if len({tuple(candidate["support"]) for candidate in support_candidates}) < 2: + selection_note += ( + " No different-support alternative was supplied, so relative_margin is " + "reported as 0." + ) + + candidate_diagnostics = [ + { + "equation": candidate_utils.format_equation(candidate["coefficients"]), + "support": list(candidate["support"]), + "training_max_error": float(candidate["max_error"]), + "condition_penalty": float(candidate["condition_ratio"]), + "relative_margin": float(candidate["relative_margin"]), + "second_best_equation": candidate["second_best_equation"], + "diagnostics": dict(candidate["diagnostics"]), + "step_penalty": float(candidate["reward_penalty"]), + } + for candidate in sorted(scored_candidates, key=lambda item: item["penalty"]) + ] + candidate_summary = ( + f"best equation={equation}; " + f"sampled-data max error={best['max_error']:.3g}; " + f"condition penalty={best['condition_ratio']:.3g}; " + f"closest different-support candidate={best['second_best_equation']}; " + f"relative margin={best['relative_margin']:.3g}" + ) + feedback = ( + "Sparse regression with thresholded ridge screening, hard-capped support " + "enumeration, and debiased coefficient refit. " + f"Selected-support kappa = {best['kappa']:.3g}; " + f"full-dictionary kappa = {dictionary_kappa:.3g}. " + f"Best fit: f(u) = {equation}. " + f"sampled-data max error = {best['max_error']:.3g}. " + f"Coefficient threshold = {coefficient_threshold:.3g}; " + f"Non-zero terms: {len(best['support'])}. " + f"condition penalty term = {best['condition_ratio']:.3g}. " + f"Closest scored different-support equation {best['second_best_equation']} " + f"has max error = {best['second_best_max_error']:.3g}; " + f"relative margin = {best['relative_margin']:.3g}. " + f"{selection_note} " + f"Candidate sampled-data diagnostics: {candidate_summary}. " + "This tool updates only the latest regression candidate; the scientific " + "conclusion inherited by future tasks is set only by update_scientific_context." + ) + return { + "support": best["support"], + "coefficients": best["coefficients"], + "equation": equation, + "max_error": best["max_error"], + "condition_ratio": best["condition_ratio"], + "relative_margin": best["relative_margin"], + "second_best_max_error": best["second_best_max_error"], + "second_best_equation": best["second_best_equation"], + "kappa": best["kappa"], + "penalty": best["reward_penalty"], + "reward_penalty": best["reward_penalty"], + "diagnostics": dict(best["diagnostics"]), + "candidate_diagnostics": candidate_diagnostics, + "feedback": feedback, + } + + +def candidate_objective_diagnostics( + candidate: str, + data: dict, + objective_data: dict, + dictionary: List[str], + dictionary_terms: List[str], + last_candidate_diagnostics: List[dict], + kappa_threshold: float, +) -> dict: + coefficients = candidate_utils.candidate_coefficients(candidate, dictionary_terms) + support = [term for term in dictionary if abs(coefficients.get(term, 0.0)) > 0.0] + pred_objective = candidate_utils.reaction_value(objective_data["u"], coefficients) + max_error = max_abs_error(objective_data["y"], pred_objective) + support_kappa = effective_kappa(data["u"], support) if support else KAPPA_CAP + condition_penalty = max( + 0.0, + math.log10(support_kappa) - math.log10(kappa_threshold), + ) + support_key = tuple(support) + alternatives = [ + item + for item in last_candidate_diagnostics + if tuple(item.get("support", [])) != support_key + ] + if alternatives: + second_best = min( + alternatives, + key=lambda item: item["training_max_error"], + ) + second_best_max_error = float(second_best["training_max_error"]) + second_best_equation = str(second_best["equation"]) + relative_margin = relative_error_margin(second_best_max_error, max_error) + else: + second_best_max_error = float("nan") + second_best_equation = "not evaluated: no alternative support supplied" + relative_margin = 0.0 + + step_penalty = max_error + diagnostics = { + "condition_ratio": condition_penalty, + "relative_margin": relative_margin, + "second_best_max_error": second_best_max_error, + "second_best_equation": second_best_equation, + } + return { + "equation": candidate, + "support": support, + "coefficients": coefficients, + "training_max_error": max_error, + "condition_penalty": condition_penalty, + "relative_margin": relative_margin, + "second_best_max_error": second_best_max_error, + "second_best_equation": second_best_equation, + "step_penalty": step_penalty, + "reward_penalty": step_penalty, + "diagnostics": diagnostics, + } diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/update_context_workflow.py b/trinity/common/workflows/connect_the_dots/pde_discovery/update_context_workflow.py new file mode 100644 index 00000000000..d2604d6df91 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/update_context_workflow.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +"""PDE-specific context update without exposing the training reward.""" + +from typing import List + +from trinity.common.workflows.connect_the_dots.update_context_workflow import ( + AsyncCoDUpdateContextWorkflow, +) + + +class PDEUpdateContextWorkflow(AsyncCoDUpdateContextWorkflow): + """Build the standard context-update prompt without its reward block.""" + + def build_messages(self) -> List[dict]: + messages = super().build_messages() + user_prompt = messages[-1]["content"] + feedback_block = ( + f"\n\nEnvironment feedback: {self.feedback}\n\n--- Your job ---" + ) + before_feedback, feedback_marker, after_feedback = user_prompt.rpartition( + feedback_block + ) + before_reward, reward_marker, reward_text = before_feedback.rpartition( + "\nReward: " + ) + if ( + not feedback_marker + or not reward_marker + or not reward_text.strip() + or "\n" in reward_text + ): + raise ValueError( + "Expected reward block was not found in context-update prompt" + ) + messages[-1]["content"] = ( + before_reward + feedback_marker + after_feedback + ) + messages[-1]["content"] += """ + +Protocol reminder: after your brief reasoning, end with exactly this block: +--- Start of updated hints --- +- Your concise, transferable hints go here. +--- End of updated hints --- +Do not shorten the opening delimiter to "--- Updated hints ---", omit either delimiter, or write anything after the end delimiter. +""" + return messages diff --git a/trinity/common/workflows/connect_the_dots/pde_discovery/workflow.py b/trinity/common/workflows/connect_the_dots/pde_discovery/workflow.py new file mode 100644 index 00000000000..a9f7f3d29fe --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/pde_discovery/workflow.py @@ -0,0 +1,1360 @@ +# -*- coding: utf-8 -*- +"""CoD PDE discovery workflow. + +This is a lightweight implementation of the PDE discovery demo in +``pde_discovery_in_CoD``. The environment accepts XML actions in plain-text +responses, keeps raw sampled points in a stateful pack buffer, and +lets the outer CoD workflow autonomously update the context after each task. +""" + +from __future__ import annotations + +import json +import logging +import math +import os +import re +import weakref +from dataclasses import dataclass, field +from typing import Dict, List, Optional, Tuple + +import numpy as np + +from trinity.common.experience import Experience +from trinity.common.models.model import ModelWrapper +from trinity.common.workflows.connect_the_dots.base_workflow import ( + AsyncCoDMultiStepWorkflow, +) +from trinity.common.workflows.connect_the_dots.pde_discovery import candidate as candidate_utils +from trinity.common.workflows.connect_the_dots.pde_discovery import ground_truth +from trinity.common.workflows.connect_the_dots.pde_discovery import pde_numeric +from trinity.common.workflows.connect_the_dots.pde_discovery import regression +from trinity.common.workflows.connect_the_dots.pde_discovery.candidate import ( + DEFAULT_DICTIONARY, +) +from trinity.common.workflows.connect_the_dots.pde_discovery.prompts import ( + load_system_prompt, + load_user_prompt, +) +from trinity.common.workflows.connect_the_dots.utils import parse_xml_answer +from trinity.common.workflows.workflow import Task + + +KAPPA_CAP = regression.KAPPA_CAP +XML_LIST_TAGS = {"dictionary", "candidate_equations", "uncertain_terms"} + +logger = logging.getLogger(__name__) + + +@dataclass +class PDEPackState: + """Persistent state shared by tasks in one CoD pack.""" + + datasets: Dict[str, dict] = field(default_factory=dict) + sample_counter: int = 0 + reaction_terms: List[str] = field(default_factory=list) + reaction_coefficients: Dict[str, float] = field(default_factory=dict) + ground_truth_template_index: Optional[int] = None + environment_seed: Optional[int] = None + latest_regression_equation: str = "" + preferred_equation: str = "" + preferred_support: List[str] = field(default_factory=list) + preferred_coefficients: Dict[str, float] = field(default_factory=dict) + dense_field: Optional[dict] = None + last_context_update: Dict[str, object] = field(default_factory=dict) + evidence_history: List[dict] = field(default_factory=list) + next_task_position: int = 1 + + +def _normalize_point_grid( + raw_grid: object, +) -> Optional[List[Tuple[float, float]]]: + if not isinstance(raw_grid, dict): + return None + + raw_points = raw_grid.get("point") + if isinstance(raw_points, dict): + raw_points = [raw_points] + if not isinstance(raw_points, list): + return None + + try: + x_values = [float(point["x"]) for point in raw_points] + t_values = [float(point["t"]) for point in raw_points] + except (KeyError, TypeError, ValueError): + return None + if not x_values or not t_values: + return None + if any(not 0.0 < value < 1.0 for value in x_values): + return None + if any(not 0.0 <= value <= 1.0 for value in t_values): + return None + if len(x_values) != len(t_values): + return None + return list(zip(x_values, t_values)) + + +class CoDPDEDiscoveryWorkflow(AsyncCoDMultiStepWorkflow): + """CoD workflow for discovering nonlinear PDE reaction terms.""" + + is_async: bool = True + # use weakref to avoid memory leakage + _PACK_STATES: weakref.WeakValueDictionary[str, PDEPackState] = ( + weakref.WeakValueDictionary() + ) + + def __init__( + self, + model: ModelWrapper, + task: Task, + auxiliary_models: Optional[List] = None, + use_openai_client: bool = False, + ): + super().__init__( + task=task, + model=model, + auxiliary_models=auxiliary_models, + use_openai_client=use_openai_client, + ) + self.reset(task) + + def reset(self, task: Task): + if isinstance(getattr(task, "workflow_args", None), dict): + task.workflow_args.setdefault("context_compression_mode", "keep_all") + super().reset(task) + args = task.workflow_args if hasattr(task, "workflow_args") else {} + self.dictionary_terms = args.get("dictionary_terms", DEFAULT_DICTIONARY) + self.min_reaction_terms = max(1, int(args.get("min_reaction_terms", 1))) + self.max_reaction_terms = max( + self.min_reaction_terms, + int(args.get("max_reaction_terms", 2)), + ) + self.dictionary_terms = [ + str(term) for term in self.dictionary_terms if str(term) in DEFAULT_DICTIONARY + ] or list(DEFAULT_DICTIONARY) + self.ground_truth_family = str( + args.get("ground_truth_family", ground_truth.DEFAULT_HIDDEN_GT_FAMILY_FILE) + ) + self.initial_condition_shape = ( + pde_numeric.InitialConditionShapeConfig.from_mapping( + args.get("initial_condition_shape") + ) + ) + if "pde_state_abs_limit" not in args: + raise ValueError("pde_state_abs_limit workflow argument is required") + self.pde_state_abs_limit = float(args["pde_state_abs_limit"]) + if ( + not math.isfinite(self.pde_state_abs_limit) + or self.pde_state_abs_limit <= 0.0 + ): + raise ValueError("pde_state_abs_limit must be finite and positive") + self.hidden_gt_templates = self._load_hidden_gt_templates() + self.initial_amplitude_upper = ( + ground_truth.calibrate_family_initial_amplitude_upper( + self.hidden_gt_templates, + self.min_reaction_terms, + self.max_reaction_terms, + state_abs_limit=self.pde_state_abs_limit, + ) + ) + self.max_steps = args.get("max_steps", 10) + self.point_budget = int(args.get("point_budget", 15)) + self.default_blind_penalty = args.get("blind_submission_penalty", 100.0) + self.kappa_threshold = float(args.get("kappa_threshold", 100.0)) + if not math.isfinite(self.kappa_threshold) or self.kappa_threshold <= 0.0: + raise ValueError("kappa_threshold must be a finite positive number") + self.noise_level = float(args.get("noise_level", 0.002)) + if not math.isfinite(self.noise_level) or self.noise_level < 0.0: + raise ValueError("noise_level must be a finite non-negative number") + self.pde_grid_size = int(args.get("pde_grid_size", 257)) + self.pde_time_steps = int(args.get("pde_time_steps", 2001)) + self.seed = int(self.raw_task.get("seed", 42)) + self.task_position = ( + int(self.raw_task.get("task_idx", task.index.get("index", 0))) + 1 + ) + self.pack_size = int(self.raw_task.get("pack_size", args.get("pack_size", 1))) + if self.pack_size <= 0: + raise ValueError("PDE task pack_size must be positive") + self.trajectory_count = self.pack_size + trajectory_index = (max(1, self.task_position) - 1) % self.trajectory_count + self.current_trajectory_id = f"traj_{trajectory_index}" + self.pack_key = self._pack_key() + + if self.task_position == 1: + self.pack_state = PDEPackState() + self._PACK_STATES[self.pack_key] = self.pack_state + elif self.pack_key in self._PACK_STATES: + self.pack_state = self._PACK_STATES[self.pack_key] + else: + raise ValueError( + "PDE pack tasks must be processed sequentially from task_idx=0" + ) + if self.task_position != self.pack_state.next_task_position: + raise ValueError( + "PDE pack task_idx values must be sequential without gaps or repeats" + ) + self._ensure_hidden_reaction() + self.pack_state.next_task_position += 1 + + self.budget_remaining = self.point_budget + self.done = False + self.current_step = 0 + self.action_feedback: Optional[str] = None + self.final_reward = 0.0 + self.last_sampled_max_error: Optional[float] = None + self.hidden_formula_l1_error: Optional[float] = None + self.hidden_formula_extra_support_penalty: Optional[float] = None + self.hidden_formula_reward_loss: Optional[float] = None + self.last_regression_dataset_id = "merged_all" + self.last_regression_dictionary = [] + self.last_candidate_diagnostics: List[dict] = [] + self.has_task_regression = False + self.has_task_explicit_sampling = False + self.has_task_context_update = False + self.early_termination_by_format_issue = False + self.dump_trajectories = bool(args.get("dump_trajectories", True)) + self.trajectory_dump_path = self._trajectory_dump_path(args) + + def _pack_key(self) -> str: + if "pack_seed" in self.raw_task: + # Runtime-injected pack_seed is the complete pack identity. Ignore + # legacy dataset pack_id so pre-generated rows remain safe when + # shuffled, mixed with another domain, or repacked at a new size. + return f"pde-seed-{self.raw_task['pack_seed']}" + return f"pde-pack-{self.task.batch_id}-{self.task.task_id}" + + def _environment_seed(self) -> int: + """Return an optional benchmark-controlled seed hidden from the model.""" + return int( + self.raw_task.get( + "pde_environment_seed", + self.raw_task.get("pack_seed", self.seed), + ) + ) + + def _support_size_hint(self) -> str: + lower = max(1, min(self.min_reaction_terms, len(self.dictionary_terms))) + upper = max(lower, min(self.max_reaction_terms, len(self.dictionary_terms))) + if lower == upper: + return f"exactly {lower}" + return f"{lower} to {upper}" + + def _ensure_hidden_reaction(self) -> None: + """Create the hidden sparse PDE reaction for this CoD pack. + + The target is deliberately kept out of task rows and prompts. It is + regenerated from the pack seed inside the environment so the agent can + only infer it through sampled-data feedback. + """ + raw_template_index = self.raw_task.get("ground_truth_template_index") + template_index = ( + int(raw_template_index) if raw_template_index is not None else None + ) + environment_seed = self._environment_seed() + if self.pack_state.reaction_coefficients: + if template_index != self.pack_state.ground_truth_template_index: + raise ValueError( + "Every task in a PDE eval pack must use the same " + "ground_truth_template_index" + ) + if environment_seed != self.pack_state.environment_seed: + raise ValueError( + "Every task in a PDE eval pack must use the same " + "pde_environment_seed" + ) + return + pack_seed = environment_seed + seed_sequence = np.random.SeedSequence( + [pack_seed, pde_numeric.GROUND_TRUTH_STREAM] + ) + rng = np.random.default_rng(seed_sequence) + min_terms = max(1, min(self.min_reaction_terms, len(self.dictionary_terms))) + max_terms = max(min_terms, min(self.max_reaction_terms, len(self.dictionary_terms))) + terms: List[str] = [] + coefficients: Dict[str, float] = {} + max_attempts = ground_truth.GT_STABILITY_MAX_ATTEMPTS + for attempt in range(max_attempts): + terms, coefficients = self._sample_hidden_reaction( + rng, + min_terms, + max_terms, + template_index=template_index, + ) + if self._is_stable_reaction_candidate(coefficients): + break + else: + logger.warning( + "No stable hidden PDE reaction found after %d attempts; " + "using the last sampled template candidate.", + max_attempts, + ) + + self.pack_state.reaction_terms = terms + self.pack_state.reaction_coefficients = coefficients + self.pack_state.ground_truth_template_index = template_index + self.pack_state.environment_seed = environment_seed + + def _load_hidden_gt_templates(self) -> List[dict]: + return ground_truth.load_hidden_gt_templates( + self.dictionary_terms, + family=self.ground_truth_family, + ) + + def _sample_hidden_reaction( + self, + rng: np.random.Generator, + min_terms: int, + max_terms: int, + template_index: Optional[int] = None, + ) -> Tuple[List[str], Dict[str, float]]: + return ground_truth.sample_hidden_reaction( + rng, + self.hidden_gt_templates, + min_terms, + max_terms, + template_index=template_index, + ) + + def _is_stable_reaction_candidate( + self, + coefficients: Dict[str, float], + ) -> bool: + return ground_truth.is_stable_reaction_candidate( + coefficients=coefficients, + pde_grid_size=self.pde_grid_size, + pde_time_steps=self.pde_time_steps, + trajectory_count=self.trajectory_count, + pack_seed=self._environment_seed(), + initial_amplitude_upper=self.initial_amplitude_upper, + initial_condition_shape=self.initial_condition_shape, + state_abs_limit=self.pde_state_abs_limit, + ) + + def _trajectory_dump_path(self, args: dict) -> str: + root = args.get("checkpoint_job_dir") or os.path.join( + "logs", "research_cod", "pde_discovery" + ) + dump_dir = os.path.join(str(root), "trajectory_dumps") + raw_uid = str(self.raw_task.get("uid", f"task_{self.task.task_id}")) + safe_uid = re.sub(r"[^A-Za-z0-9_.-]+", "_", raw_uid) + safe_pack = re.sub(r"[^A-Za-z0-9_.-]+", "_", self.pack_key) + file_name = ( + f"{safe_pack}_{safe_uid}_pos{self.task_position}_" + f"pid{os.getpid()}_obj{id(self)}.jsonl" + ) + return os.path.join(dump_dir, file_name) + + def _dump_trajectory_record(self, record: dict) -> None: + if not self.dump_trajectories: + return + try: + os.makedirs(os.path.dirname(self.trajectory_dump_path), exist_ok=True) + payload = { + "uid": self.raw_task.get("uid"), + "pack_key": self.pack_key, + "task_position": self.task_position, + "event": record.get("event"), + "step": record.get("step"), + "budget_remaining": self.budget_remaining, + "latest_regression_equation": self.pack_state.latest_regression_equation, + "current_equation": self.pack_state.preferred_equation, + "reward_snapshot": self.final_reward, + "format_error": self.early_termination_by_format_issue, + **record, + } + with open(self.trajectory_dump_path, "a", encoding="utf-8") as f: + f.write(json.dumps(payload, ensure_ascii=False) + "\n") + except Exception as exc: + logger.warning("Failed to write PDE trajectory dump: %s", exc) + + async def run_async(self) -> List[Experience]: + self.memory.clear() + sys_prompt = load_system_prompt( + dictionary_terms=self.dictionary_terms, + support_size_hint=self._support_size_hint(), + ) + sys_prompt = self._augment_system_prompt(sys_prompt) + self.memory.append({"role": "system", "content": sys_prompt}) + try: + return await super().run_async() + except BaseException: + self._PACK_STATES.pop(self.pack_key, None) + raise + + async def step_async(self, step_num: int) -> Tuple[bool, List[Experience]]: + if self.done: + return False, [] + + user_content = load_user_prompt( + task_position=self.task_position, + pack_size=self.pack_size, + current_step=step_num + 1, + max_steps=self.max_steps, + point_budget=self.point_budget, + budget_remaining=self.budget_remaining, + task_description=self.task_desc or "", + available_datasets=sorted(self.pack_state.datasets.keys()), + last_sampled_max_error=self.last_sampled_max_error, + evidence_memory=self.pack_state.last_context_update.get( + "evidence_memory", {} + ), + action_feedback=self.action_feedback, + ) + if self.icl_examples and step_num == 0: + user_content = ( + f"{user_content}\n\nHere are some reference examples:\n\n" + f"{self.icl_examples}" + ) + + self.memory.append({"role": "user", "content": user_content}) + if self.reply_prefix: + self.memory.append({"role": "assistant", "content": self.reply_prefix}) + + experiences = await self.model.chat_async(self.memory, **self.rollout_args) + response_text = experiences[0].response_text or "" + self.memory.append({"role": "assistant", "content": response_text}) + + sys_prompt = self.memory[0]["content"] if self.memory else "" + for exp in experiences: + exp.info["sys_prompt"] = sys_prompt + exp.info["user_prompt"] = user_content + + action, parse_error = parse_xml_answer(response_text, XML_LIST_TAGS) + if action is None: + response_excerpt = response_text.replace("\n", "\\n")[:500] + logger.warning( + "PDE discovery format error (%s). " + "Response excerpt: %s", + parse_error, + response_excerpt, + ) + for exp in experiences: + exp.info["pde_parse_error"] = parse_error + exp.info["pde_parse_error_response_excerpt"] = response_excerpt + self.action_feedback = ( + "Invalid format: expected exactly one XML action wrapped in " + ".... Game over." + ) + self.final_reward = 0.0 + self.done = True + self.current_step = step_num + 1 + self.early_termination_by_format_issue = True + terminated = True + format_error = True + feedback = self.action_feedback + else: + feedback, terminated, format_error = self._execute_action(action) + self.action_feedback = feedback + self.done = terminated + self.current_step = step_num + 1 + self.early_termination_by_format_issue = format_error + if format_error: + self.final_reward = 0.0 + + self._dump_trajectory_record( + { + "event": "step", + "step": step_num + 1, + "response": response_text, + "action": action, + "action_feedback": feedback, + "terminated": terminated, + "format_error": format_error, + "parse_error": parse_error, + "reward_snapshot": self.final_reward, + } + ) + return not self.done and self.current_step < self.max_steps, experiences + + def _execute_action(self, payload: dict) -> Tuple[str, bool, bool]: + action = payload.get("action") + args = payload.get("args", {}) + if not isinstance(args, dict): + return "Invalid action arguments. Game over.", True, True + + handlers = { + "sample_pde_data": self._action_sample_pde_data, + "summarize_pack_evidence": self._action_summarize_pack_evidence, + "run_sparse_regression": self._action_run_sparse_regression, + "update_scientific_context": self._action_update_scientific_context, + } + handler = handlers.get(action) + if handler is None: + return f"Invalid action: unknown action '{action}'. Game over.", True, True + return handler(args) + + def _action_summarize_pack_evidence(self, args: dict) -> Tuple[str, bool, bool]: + reveal_value = args.get("reveal_equations", "false") + if str(reveal_value).lower() not in {"true", "false"}: + return "Error: reveal_equations must be true or false.", False, False + reveal_equations = str(reveal_value).lower() == "true" + history = list(self.pack_state.evidence_history) + if not history: + return ( + "Pack evidence summary: no prior task evidence has been recorded " + "in this pack. Start with current-task measurements.", + False, + False, + ) + + slots: Dict[str, dict] = {} + for entry in history: + equation = str(entry.get("preferred_equation") or "").strip() + if not equation: + continue + if equation not in slots: + slots[equation] = { + "slot_id": f"H{len(slots) + 1}", + "equation": equation, + "tasks": set(), + } + slot = slots[equation] + if entry.get("task_position") is not None: + slot["tasks"].add(str(entry["task_position"])) + + if not slots: + return ( + "Pack evidence summary: prior notes exist, but no tested " + "candidate slots are available. Use current-task actions to build evidence.", + False, + False, + ) + + lines = [ + "Pack evidence summary; historical hypotheses, not ground truth.", + ] + for equation, slot in slots.items(): + pieces = [ + f"{slot['slot_id']}: tasks={','.join(sorted(slot['tasks'])) or 'unknown'}", + ] + if reveal_equations: + pieces.append(f"agent_recorded_equation={slot['equation']}") + lines.append("; ".join(pieces) + ".") + + if not reveal_equations: + lines.append( + "Call summarize_pack_evidence with reveal_equations=true only if " + "you need to actively reuse prior agent-recorded candidates." + ) + return "\n".join(lines), False, False + + def _action_sample_pde_data(self, args: dict) -> Tuple[str, bool, bool]: + has_points = args.get("points") is not None + has_point_grid = args.get("point_grid") is not None + if has_points: + return ( + "Error: explicit points are not supported. Use point_grid with " + "one point element per numeric x and t coordinate pair.", + False, + False, + ) + grid_points = _normalize_point_grid(args.get("point_grid")) + if not has_point_grid: + return ( + "Error: sample_pde_data supports only point_grid. Provide " + "one point element per numeric x and t coordinate pair.", + False, + False, + ) + if has_point_grid and grid_points is None: + return ( + "Error: each point in point_grid must contain numeric x and t " + "values with 0 < x < 1 and 0 <= t <= 1.", + False, + False, + ) + + grid_sample_points = grid_points or [] + num_points = len(grid_sample_points) + if num_points <= 0: + return "Error: point_grid must produce at least one point.", False, False + if num_points > self.budget_remaining: + return ( + f"Error: requested {num_points} points but only " + f"{self.budget_remaining} budget remains.", + False, + False, + ) + + trajectory_id = self.current_trajectory_id + self.pack_state.sample_counter += 1 + dataset_id = f"ds_t{self.task_position}_{self.pack_state.sample_counter}" + data = self._sample_grid_points(grid_sample_points, trajectory_id) + x_values = data["x"] + t_values = data["t"] + region = { + "x": [float(np.min(x_values)), float(np.max(x_values))], + "t": [float(np.min(t_values)), float(np.max(t_values))], + } + sampling_mode = "point_grid" + self.has_task_explicit_sampling = True + data["region"] = region + data["sampling_strategy"] = sampling_mode + data["task_position"] = self.task_position + data["trajectory_id"] = np.array([trajectory_id] * num_points, dtype=object) + self.pack_state.datasets[dataset_id] = data + self.budget_remaining -= num_points + + t = data["t"] + summary = ( + f"Sampled {num_points} points. Dataset: {dataset_id}. " + f"Point budget remaining: {self.budget_remaining}/{self.point_budget}. " + f"Sampling mode: {sampling_mode}. " + f"Region: x in [{region['x'][0]:.3g}, {region['x'][1]:.3g}], " + f"t in [{region['t'][0]:.3g}, {region['t'][1]:.3g}]. " + f"mean t = {np.mean(t):.3g}. " + f"Dataset coverage: {self._coverage_report(data)}." + ) + return summary, False, False + + def _action_run_sparse_regression(self, args: dict) -> Tuple[str, bool, bool]: + dataset_id = str(args.get("dataset_id", "merged_all")) + data = self._resolve_dataset(dataset_id) + if data is None: + return f"Error: dataset_id '{dataset_id}' was not found.", False, False + + raw_dictionary = args.get("dictionary") + raw_candidates = args.get("candidate_equations") + has_candidates = isinstance(raw_candidates, list) and bool(raw_candidates) + if has_candidates and len(raw_candidates) > 8: + return "Error: candidate_equations accepts at most 8 equations.", False, False + if raw_dictionary is None and not has_candidates: + return ( + "Error: run_sparse_regression requires an agent-specified " + "dictionary or candidate_equations.", + False, + False, + ) + if raw_dictionary is not None and ( + not isinstance(raw_dictionary, list) or not raw_dictionary + ): + return ( + "Error: dictionary must be a non-empty list of supported terms.", + False, + False, + ) + dictionary = self._sanitize_dictionary(raw_dictionary) + if raw_dictionary is not None and not any( + str(term) in self.dictionary_terms for term in raw_dictionary + ): + return ( + "Error: dictionary did not contain any supported basis terms.", + False, + False, + ) + allowed_supports = None + if has_candidates: + candidate_strings = [ + str(candidate).strip() + for candidate in raw_candidates[:8] + if str(candidate).strip() + ] + allowed_supports = [] + for candidate in candidate_strings: + support = self._candidate_support(candidate, dictionary) + if support: + allowed_supports.append(support) + if not allowed_supports: + return ( + "Error: candidate_equations did not contain any supported " + "dictionary terms.", + False, + False, + ) + if raw_dictionary is None: + support_terms = { + term for support in allowed_supports for term in support + } + dictionary = [ + term + for term in self.dictionary_terms + if term in support_terms + ] + if not dictionary: + return ( + "Error: run_sparse_regression has no selected supported terms " + "to score.", + False, + False, + ) + alpha_arg = args.get("alpha") + threshold_arg = args.get("threshold") + try: + alpha = float(0.05 if alpha_arg is None else alpha_arg) + threshold = float(0.05 if threshold_arg is None else threshold_arg) + except (TypeError, ValueError): + return ( + "Error: alpha and threshold must be numeric when provided.", + False, + False, + ) + if not all( + math.isfinite(value) and value >= 0.0 for value in (alpha, threshold) + ): + return ( + "Error: alpha and threshold must be finite non-negative numbers.", + False, + False, + ) + result = self._regression_result( + data["u"], + data["y"], + data["u"], + data["y"], + dictionary, + alpha, + threshold, + allowed_supports=allowed_supports, + ) + result["feedback"] += ( + f" Fit dataset coverage: {self._coverage_report(data)}. " + "Objective scope: current sampled dataset only; use it as a local " + "fit and conditioning check before committing an equation. " + f"{self._point_error_report(data, result['coefficients'])}" + ) + self.last_sampled_max_error = result["penalty"] + self.last_regression_dataset_id = dataset_id + self.last_regression_dictionary = list(dictionary) + self.last_candidate_diagnostics = result.get("candidate_diagnostics", []) + self.pack_state.latest_regression_equation = result["equation"] + self.has_task_regression = True + return result["feedback"], False, False + + def _task_local_observation_stats( + self, + data: Optional[dict], + ) -> dict: + if data is None: + return {"n": 0, "u_span": 0.0} + u_values = np.asarray(data.get("u", []), dtype=float) + u_values = u_values[np.isfinite(u_values)] + if len(u_values) == 0: + return {"n": 0, "u_span": 0.0} + return { + "n": int(len(u_values)), + "u_span": float(np.max(u_values) - np.min(u_values)), + } + + def _structured_evidence_memory( + self, + *, + preferred_diagnostics: dict, + observation_stats: dict, + ) -> dict: + preferred_error = float( + preferred_diagnostics.get("training_max_error", KAPPA_CAP) + ) + + return { + "task_evidence_status": "recorded", + "observation_summary": { + "n": observation_stats.get("n", 0), + "u_span": observation_stats.get("u_span", 0.0), + "relative_margin": preferred_diagnostics.get("relative_margin", 0.0), + "preferred_sampled_max_error": preferred_error, + }, + } + + def _action_update_scientific_context(self, args: dict) -> Tuple[str, bool, bool]: + short_note = re.sub(r"\s+", " ", str(args.get("note", "") or "")).strip() + preferred_equation = str(args.get("preferred_equation", "")).strip() + uncertain_terms = args.get("uncertain_terms", []) + if not isinstance(uncertain_terms, list): + uncertain_terms = [str(uncertain_terms)] + if len(uncertain_terms) > 4: + return "Error: uncertain_terms accepts at most 4 terms.", False, False + uncertain_terms = [ + re.sub(r"\s+", " ", str(term or "")).strip() + for term in uncertain_terms + if str(term or "").strip() + ] + if not preferred_equation: + return ( + "Error: update_scientific_context requires preferred_equation. " + "Choose the equation you want to finalize from the sampled-data " + "diagnostics. This may be the " + "latest regression candidate or another candidate scored by " + "run_sparse_regression.", + False, + False, + ) + for field_name, equation in ( + ("preferred_equation", preferred_equation), + ): + symbolic_tokens = candidate_utils.symbolic_coefficient_tokens(equation) + if symbolic_tokens: + tokens = ", ".join(symbolic_tokens) + return ( + f"Error: {field_name} contains symbolic coefficient(s): " + f"{tokens}. update_scientific_context requires explicit " + "numeric coefficients from a tested candidate, not " + "placeholders such as c1/c2/a/b.", + False, + False, + ) + if not self.has_task_explicit_sampling: + return ( + "Error: update_scientific_context requires current-task " + "sample_pde_data evidence in the current task. Choose " + "point_grid coordinates, sample them, then run sparse " + "regression before updating scientific context.", + False, + False, + ) + if not self.has_task_regression: + return ( + "Error: update_scientific_context requires at least one " + "run_sparse_regression call in the current task. Run regression " + "on an available dataset first so the task has a current " + "equation and sampled-data evidence.", + False, + False, + ) + preferred_coefficients = self._candidate_coefficients(preferred_equation) + if not preferred_coefficients: + return ( + "Error: preferred_equation did not parse into dictionary terms. " + "Use an explicit equation built from supported dictionary terms " + "with numeric coefficients.", + False, + False, + ) + evaluated_candidates = [ + self._candidate_coefficients(str(item.get("equation", ""))) + for item in self.last_candidate_diagnostics + ] + if not any( + all( + math.isclose( + preferred_coefficients.get(term, 0.0), + candidate.get(term, 0.0), + rel_tol=1e-9, + abs_tol=1e-12, + ) + for term in self.dictionary_terms + ) + for candidate in evaluated_candidates + ): + return ( + "Error: preferred_equation must match a candidate scored by the " + "latest run_sparse_regression call.", + False, + False, + ) + data = self._resolve_dataset(self.last_regression_dataset_id) + if data is None: + data = self._resolve_dataset("merged_all") + preferred_diagnostics = self._candidate_objective_diagnostics( + preferred_equation, + data, + self.last_regression_dictionary, + ) + preferred_support = list(preferred_diagnostics["support"]) + observation_stats = self._task_local_observation_stats(data) + reward_diagnostics = preferred_diagnostics + self.last_sampled_max_error = float(reward_diagnostics["step_penalty"]) + hidden_reward_diagnostics = self._hidden_formula_reward_diagnostics( + preferred_equation + ) + self.hidden_formula_l1_error = float( + hidden_reward_diagnostics["normalized_l1_error"] + ) + self.hidden_formula_extra_support_penalty = float( + hidden_reward_diagnostics["extra_support_penalty"] + ) + self.hidden_formula_reward_loss = float(hidden_reward_diagnostics["loss"]) + self.pack_state.preferred_equation = preferred_equation + self.pack_state.preferred_support = preferred_support + self.pack_state.preferred_coefficients = dict( + preferred_diagnostics["coefficients"] + ) + self.final_reward = float(hidden_reward_diagnostics["reward"]) + evidence_memory = self._structured_evidence_memory( + preferred_diagnostics=preferred_diagnostics, + observation_stats=observation_stats, + ) + preferred_diagnostics_summary = { + "training_max_error": reward_diagnostics["training_max_error"], + "condition_penalty": reward_diagnostics["condition_penalty"], + "relative_margin": reward_diagnostics["relative_margin"], + "second_best_equation": reward_diagnostics["second_best_equation"], + "step_penalty": reward_diagnostics["step_penalty"], + } + self.pack_state.last_context_update = { + "evidence_memory": evidence_memory, + "preferred_equation": preferred_equation, + "preferred_diagnostics": preferred_diagnostics_summary, + "latest_regression_equation": self.pack_state.latest_regression_equation, + "note": short_note, + "uncertain_terms": [str(term) for term in uncertain_terms], + } + self.pack_state.evidence_history.append( + { + "task_position": self.task_position, + "evidence_memory": dict(evidence_memory), + "preferred_equation": preferred_equation, + } + ) + self.pack_state.evidence_history = self.pack_state.evidence_history[-self.pack_size :] + self.has_task_context_update = True + return ( + "Task terminated. " + f"reward_equation={preferred_equation}; " + f"sampled-data max error={reward_diagnostics['training_max_error']:.3g}; " + f"condition penalty={reward_diagnostics['condition_penalty']:.3g}; " + f"closest different-support candidate={reward_diagnostics['second_best_equation']}; " + f"relative margin={reward_diagnostics['relative_margin']:.3g}.", + True, + False, + ) + + def _sample_grid_points( + self, + points: List[Tuple[float, float]], + trajectory_id: str, + ) -> dict: + seed_sequence = np.random.SeedSequence( + [ + self.seed, + self.task_position, + self.pack_state.sample_counter, + ] + ) + rng = np.random.default_rng(seed_sequence) + self._ensure_dense_field() + x = np.array([point[0] for point in points], dtype=float) + t = np.array([point[1] for point in points], dtype=float) + return self._sample_values_at_points(x, t, trajectory_id, rng) + + def _sample_values_at_points( + self, + x: np.ndarray, + t: np.ndarray, + trajectory_id: str, + rng: np.random.Generator, + ) -> dict: + # The regression target is the scheme-consistent reaction residual, + # sampled from the dense hidden PDE field rather than direct oracle f(u). + u = self._interpolate_dense_field("u", x, t, trajectory_id) + y = self._interpolate_dense_field("y", x, t, trajectory_id) + point_count = len(x) + u += rng.normal(0.0, self.noise_level, point_count) + y += rng.normal(0.0, self.noise_level * 0.25, point_count) + return {"x": x, "t": t, "u": u, "y": y} + + def _ensure_dense_field(self) -> None: + if self.pack_state.dense_field is not None: + return + + nx = max(17, self.pde_grid_size) + nt = max(51, self.pde_time_steps) + if nx % 2 == 0: + nx += 1 + x_grid = np.linspace(0.0, 1.0, nx) + t_grid = np.linspace(0.0, 1.0, nt) + dx = float(x_grid[1] - x_grid[0]) + dt = float(t_grid[1] - t_grid[0]) + r = dt / (dx * dx) + + interior = nx - 2 + lower = -r * np.ones(interior - 1, dtype=float) + diag = (1.0 + 2.0 * r) * np.ones(interior, dtype=float) + upper = -r * np.ones(interior - 1, dtype=float) + + trajectories = {} + for traj_idx in range(self.trajectory_count): + trajectory_id = f"traj_{traj_idx}" + trajectories[trajectory_id] = self._simulate_dense_trajectory( + x_grid, + t_grid, + dx, + dt, + lower, + diag, + upper, + traj_idx, + ) + + default_field = trajectories["traj_0"] + self.pack_state.dense_field = { + "trajectories": trajectories, + "x_grid": x_grid, + "t_grid": t_grid, + "u": default_field["u"], + "y": default_field["y"], + } + + def _simulate_dense_trajectory( + self, + x_grid: np.ndarray, + t_grid: np.ndarray, + dx: float, + dt: float, + lower: np.ndarray, + diag: np.ndarray, + upper: np.ndarray, + trajectory_index: int, + ) -> dict: + return pde_numeric.simulate_dense_trajectory( + x_grid=x_grid, + t_grid=t_grid, + dx=dx, + dt=dt, + lower=lower, + diag=diag, + upper=upper, + trajectory_index=trajectory_index, + reaction_fn=self._reaction_value, + pack_seed=self._environment_seed(), + trajectory_count=self.trajectory_count, + initial_amplitude_upper=self.initial_amplitude_upper, + initial_condition_shape=self.initial_condition_shape, + state_abs_limit=self.pde_state_abs_limit, + ) + + def _interpolate_dense_field( + self, + field_name: str, + x: np.ndarray, + t: np.ndarray, + trajectory_id: str = "traj_0", + ) -> np.ndarray: + self._ensure_dense_field() + return pde_numeric.interpolate_dense_field( + self._trajectory_field(trajectory_id), + field_name, + x, + t, + ) + + def _trajectory_field(self, trajectory_id: str) -> dict: + self._ensure_dense_field() + dense_field = self.pack_state.dense_field or {} + trajectories = dense_field.get("trajectories") + if isinstance(trajectories, dict) and trajectory_id in trajectories: + return trajectories[trajectory_id] + return dense_field + + def _sanitize_dictionary(self, dictionary: object) -> List[str]: + return candidate_utils.sanitize_dictionary(dictionary, self.dictionary_terms) + + def _reaction_value( + self, u: np.ndarray, coefficients: Optional[Dict[str, float]] = None + ) -> np.ndarray: + return candidate_utils.reaction_value( + u, + coefficients or self.pack_state.reaction_coefficients, + ) + + def _resolve_dataset(self, dataset_id: str) -> Optional[dict]: + if dataset_id in self.pack_state.datasets: + return self.pack_state.datasets[dataset_id] + if dataset_id == "merged_all" or dataset_id == "merged": + ids = sorted(self.pack_state.datasets) + else: + match = re.fullmatch(r"merged_t(\d+)_t(\d+)", dataset_id) + if not match: + return None + lo, hi = int(match.group(1)), int(match.group(2)) + ids = [ + dsid + for dsid, data in self.pack_state.datasets.items() + if lo <= int(data.get("task_position", 0)) <= hi + ] + if not ids: + return None + merged = { + key: np.concatenate([self.pack_state.datasets[dsid][key] for dsid in ids]) + for key in ["x", "t", "u", "y"] + } + if all("trajectory_id" in self.pack_state.datasets[dsid] for dsid in ids): + merged["trajectory_id"] = np.concatenate( + [self.pack_state.datasets[dsid]["trajectory_id"] for dsid in ids] + ) + return merged + + def _coverage_report(self, data: dict) -> str: + u = np.asarray(data.get("u", []), dtype=float) + if len(u) == 0: + return "empty dataset" + x = np.asarray(data.get("x", []), dtype=float) + t = np.asarray(data.get("t", []), dtype=float) + pieces = [ + f"n={len(u)}", + f"u=[{np.min(u):.3g},{np.max(u):.3g}]", + ] + if len(x): + pieces.append(f"x_span={np.max(x) - np.min(x):.3g}") + if len(t): + pieces.append(f"t_span={np.max(t) - np.min(t):.3g}") + return "; ".join(pieces) + + def _point_error_report(self, data: dict, coefficients: Dict[str, float]) -> str: + x = np.asarray(data.get("x", []), dtype=float) + t = np.asarray(data.get("t", []), dtype=float) + u = np.asarray(data.get("u", []), dtype=float) + y = np.asarray(data.get("y", []), dtype=float) + if not (len(x) == len(t) == len(u) == len(y)) or len(u) == 0: + return "Sampled-point errors: unavailable." + pred = self._reaction_value(u, coefficients=coefficients) + errors = np.abs(y - pred) + rows = [ + ( + f"(x={float(x_i):.4g}, t={float(t_i):.4g}, " + f"u={float(u_i):.4g}, y={float(y_i):.4g}, " + f"pred={float(pred_i):.4g}, abs_error={float(err_i):.4g})" + ) + for x_i, t_i, u_i, y_i, pred_i, err_i in zip(x, t, u, y, pred, errors) + ] + return "Sampled-point errors: " + "; ".join(rows) + "." + + def _hidden_formula_reward_diagnostics(self, candidate: str) -> dict: + """Compute the training-only reward from hidden coefficient vectors.""" + pred_coefficients = self._candidate_coefficients(candidate) + target_coefficients = self.pack_state.reaction_coefficients + target_l1 = sum(abs(float(value)) for value in target_coefficients.values()) + diff_l1 = 0.0 + for term in self.dictionary_terms: + diff_l1 += abs( + float(pred_coefficients.get(term, 0.0)) + - float(target_coefficients.get(term, 0.0)) + ) + normalized_l1 = diff_l1 / max(1e-8, target_l1) + pred_support_size = sum( + 1 for value in pred_coefficients.values() if abs(float(value)) > 1e-12 + ) + target_support_size = len(self.pack_state.reaction_terms) + extra_support_penalty = float( + max(0, pred_support_size - target_support_size) + ) + loss = float(normalized_l1 + extra_support_penalty) + return { + "normalized_l1_error": float(normalized_l1), + "extra_support_penalty": extra_support_penalty, + "loss": loss, + "reward": float(1.0 / (1.0 + loss)), + } + + def _regression_result( + self, + u: np.ndarray, + y: np.ndarray, + u_objective: np.ndarray, + y_objective: np.ndarray, + dictionary: list, + alpha: float, + threshold: float, + allowed_supports: Optional[List[List[str]]] = None, + ) -> dict: + return regression.sparse_regression_result( + u=u, + y=y, + u_objective=u_objective, + y_objective=y_objective, + dictionary=dictionary, + alpha=alpha, + threshold=threshold, + max_reaction_terms=self.max_reaction_terms, + default_blind_penalty=self.default_blind_penalty, + kappa_threshold=self.kappa_threshold, + allowed_supports=allowed_supports, + ) + + def _candidate_coefficients(self, candidate: str) -> Dict[str, float]: + return candidate_utils.candidate_coefficients( + candidate, + self.dictionary_terms, + ) + + def _candidate_support(self, candidate: str, dictionary: List[str]) -> List[str]: + return candidate_utils.candidate_support( + candidate, + dictionary, + self.dictionary_terms, + ) + + def _candidate_objective_diagnostics( + self, + candidate: str, + sampled_data: dict, + dictionary: List[str], + ) -> dict: + return regression.candidate_objective_diagnostics( + candidate=candidate, + data=sampled_data, + objective_data=sampled_data, + dictionary=dictionary, + dictionary_terms=self.dictionary_terms, + last_candidate_diagnostics=self.last_candidate_diagnostics, + kappa_threshold=self.kappa_threshold, + ) + + def _get_feedback(self) -> str: + if self.early_termination_by_format_issue: + return self.action_feedback or "Invalid action." + return "\n".join( + [ + "Latest sampled-data diagnostic: " + f"{self.last_sampled_max_error if self.last_sampled_max_error is not None else 'none'}", + ( + "Latest finalized equation: " + f"{self.pack_state.preferred_equation or 'none'}" + ), + ( + "Latest regression candidate: " + f"{self.pack_state.latest_regression_equation or 'none'}" + ), + ] + ) + + def _hidden_accuracy_metrics(self) -> dict: + if not self.has_task_context_update: + return { + "structure_recovery": 0.0, + "coefficient_relative_error": 1.0, + } + coeffs = self.pack_state.preferred_coefficients + target = self.pack_state.reaction_coefficients + target_support = set(self.pack_state.reaction_terms) + support = self.pack_state.preferred_support + structure_recovery = float(set(support) == target_support) + coefficient_error = 1.0 + if structure_recovery: + coefficient_error = sum( + abs(coeffs.get(term, 0.0) - target[term]) + / max(1e-6, abs(target[term])) + for term in target_support + ) / max(1, len(target_support)) + return { + "structure_recovery": structure_recovery, + "coefficient_relative_error": coefficient_error, + } + + async def reward_async(self, exps: List[Experience]) -> float: + if not self.early_termination_by_format_issue and not self.has_task_context_update: + self.final_reward = 0.0 + if self.last_sampled_max_error is None: + self.last_sampled_max_error = self.default_blind_penalty + self.action_feedback = ( + self.action_feedback or "" + ) + "\nTask ended without update_scientific_context; no scientific context was committed." + hidden_metrics = self._hidden_accuracy_metrics() + reward = await super().reward_async(exps) + if not exps: + if self.task_position >= self.pack_size: + self._PACK_STATES.pop(self.pack_key, None) + return reward + + metrics = exps[-1].metrics or {} + metrics.update( + { + "pde_structure_recovery": hidden_metrics["structure_recovery"], + "pde_coefficient_relative_error": hidden_metrics[ + "coefficient_relative_error" + ], + "pde_sample_efficiency_points": float( + self.point_budget - self.budget_remaining + ), + "pde_last_sampled_max_error": float( + self.last_sampled_max_error + if self.last_sampled_max_error is not None + else self.default_blind_penalty + ), + "pde_hidden_formula_normalized_l1_error": float( + self.hidden_formula_l1_error + if self.hidden_formula_l1_error is not None + else self.default_blind_penalty + ), + "pde_hidden_formula_extra_support_penalty": float( + self.hidden_formula_extra_support_penalty + if self.hidden_formula_extra_support_penalty is not None + else 0.0 + ), + "pde_hidden_formula_reward_loss": float( + self.hidden_formula_reward_loss + if self.hidden_formula_reward_loss is not None + else self.default_blind_penalty + ), + "pde_ground_truth_support_size": float( + len(self.pack_state.reaction_terms) + ), + "pde_ground_truth_template_index": float( + self.pack_state.ground_truth_template_index + if self.pack_state.ground_truth_template_index is not None + else -1 + ), + "pde_ground_truth_template_instance": float( + self.raw_task.get("ground_truth_template_instance", -1) + ), + "pde_eval_pack_index": float( + self.raw_task.get("eval_pack_index", -1) + ), + } + ) + support_size = len(self.pack_state.reaction_terms) + metrics.update( + { + f"pde_structure_recovery_support_size_{support_size}": hidden_metrics[ + "structure_recovery" + ], + f"pde_hidden_formula_l1_support_size_{support_size}": float( + self.hidden_formula_l1_error + if self.hidden_formula_l1_error is not None + else self.default_blind_penalty + ), + f"pde_hidden_formula_reward_support_size_{support_size}": float( + self.final_reward + ), + } + ) + template_index = self.pack_state.ground_truth_template_index + if template_index is not None: + metrics.update( + { + f"pde_structure_recovery_template_{template_index}": hidden_metrics[ + "structure_recovery" + ], + f"pde_hidden_formula_l1_template_{template_index}": float( + self.hidden_formula_l1_error + if self.hidden_formula_l1_error is not None + else self.default_blind_penalty + ), + f"pde_hidden_formula_reward_template_{template_index}": float( + self.final_reward + ), + } + ) + exps[-1].metrics = metrics + self._dump_trajectory_record( + { + "event": "final", + "step": self.current_step, + "reward": reward, + "final_reward": self.final_reward, + "feedback": self._get_feedback(), + "metrics": dict(metrics), + } + ) + if self.task_position >= self.pack_size: + self._PACK_STATES.pop(self.pack_key, None) + return reward + + def _compress_memory(self) -> None: + super()._compress_memory() + old_feedback_cutoff = max(1, self.current_step - 1) + seen_user_messages = 0 + for msg in self.memory: + if msg.get("role") != "user": + continue + seen_user_messages += 1 + content = msg.get("content", "") + if seen_user_messages >= old_feedback_cutoff or "Action feedback:" not in content: + continue + msg["content"] = ( + content.split("Action feedback:")[0] + + "Action feedback:\n[History compressed.]" + ) + + @property + def max_step_num(self) -> int: + return self.max_steps diff --git a/trinity/common/workflows/connect_the_dots/terminal/__init__.py b/trinity/common/workflows/connect_the_dots/terminal/__init__.py new file mode 100644 index 00000000000..0e5518b1bc8 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/terminal/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +"""Simulated terminal environment for RL training of file operation skills.""" diff --git a/trinity/common/workflows/connect_the_dots/terminal/commands.py b/trinity/common/workflows/connect_the_dots/terminal/commands.py new file mode 100644 index 00000000000..ec7a2df5ab1 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/terminal/commands.py @@ -0,0 +1,1101 @@ +# -*- coding: utf-8 -*- +""" +Command handlers for the simulated terminal environment. + +Each handler is a class with an ``execute(args, env)`` method that returns +a string (the terminal output). Handlers read/write the virtual filesystem +via ``env.current_machine.fs`` and ``env.current_machine`` state. +""" + +from __future__ import annotations + +import json +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Dict, List, Set + +from trinity.common.workflows.connect_the_dots.terminal.env import OSType + +if TYPE_CHECKING: + from trinity.common.workflows.connect_the_dots.terminal.env import TerminalEnv + + +# --------------------------------------------------------------------------- +# Base +# --------------------------------------------------------------------------- + +class CommandHandler(ABC): + """Base class for command handlers.""" + + name: str = "" + # OS where this command is available. Empty set = all OS. + available_os: Set[OSType] = set() + # If True, command can only run when NOT ssh-connected (on local machine). + local_only: bool = False + + def is_available(self, os_type: OSType, ssh_connected: bool) -> bool: + if self.local_only and ssh_connected: + return False + if ssh_connected: + # Remote is always Linux + return True + if self.available_os and os_type not in self.available_os: + return False + return True + + @abstractmethod + def execute(self, args: List[str], env: TerminalEnv) -> str: + ... + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _machine(env: TerminalEnv): + return env.current_machine + + +def _resolve(env: TerminalEnv, path: str) -> str: + return _machine(env).resolve_path(path) + + +def _display(env: TerminalEnv, memfs_path: str) -> str: + return _machine(env).to_display_path(memfs_path) + + +def _format_size(size: int) -> str: + if size < 1024: + return f"{size}" + elif size < 1024 * 1024: + return f"{size / 1024:.1f}K" + else: + return f"{size / (1024 * 1024):.1f}M" + + +def _parse_scp_target(arg: str): + """Parse ``user@host:path``, ``user@host:``, or ``user@host``. Returns (user, host, path) or (None, None, path).""" + if ":" in arg and "@" in arg.split(":")[0]: + user_host, path = arg.split(":", 1) + user, host = user_host.split("@", 1) + return user, host, path + # user@host without colon — treat as remote home directory + if "@" in arg and "/" not in arg and "\\" not in arg: + user, host = arg.split("@", 1) + return user, host, "" + return None, None, arg + + +# --------------------------------------------------------------------------- +# ls +# --------------------------------------------------------------------------- + +class LsHandler(CommandHandler): + name = "ls" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + m = _machine(env) + fs = m.fs + + show_long = False + show_all = False + paths = [] + for a in args: + if a.startswith("--"): + if a == "--all": + show_all = True + elif a == "--long": + show_long = True + elif a.startswith("-"): + for ch in a[1:]: + if ch == "l": + show_long = True + elif ch == "a": + show_all = True + else: + paths.append(a) + + if not paths: + paths = [m.to_display_path(m.cwd)] + + all_output = [] + for p in paths: + mp = _resolve(env, p) + if not fs.exists(mp): + all_output.append(f"ls: cannot access '{p}': No such file or directory") + continue + if fs.isfile(mp): + if show_long: + all_output.append(self._long_entry(fs, mp, mp.split("/")[-1])) + else: + all_output.append(mp.split("/")[-1]) + continue + try: + entries = fs.listdir(mp) + except Exception: + all_output.append(f"ls: cannot access '{p}': No such file or directory") + continue + + if not show_all: + entries = [e for e in entries if not e.startswith(".")] + + # Truncate long listings + truncated = False + if len(entries) > 50: + total = len(entries) + entries = entries[:50] + truncated = True + + if show_long: + lines = [] + for name in entries: + child = f"{mp}/{name}" if mp else name + lines.append(self._long_entry(fs, child, name)) + if truncated: + lines.append(f"... ({total - 50} more entries)") + all_output.append("\n".join(lines)) + else: + if m.os_type == OSType.WINDOWS and not env.ssh_connected: + all_output.append(self._dir_format(fs, mp, entries, truncated, + total if truncated else len(entries))) + else: + line = " ".join( + f"{name}/" if fs.isdir(f"{mp}/{name}" if mp else name) else name + for name in entries + ) + if truncated: + line += f"\n... ({total - 50} more entries)" + all_output.append(line) + + return "\n".join(all_output) + + def _long_entry(self, fs, memfs_path, name): + meta = fs.get_meta(memfs_path) + is_dir = fs.isdir(memfs_path) + perm_str = "d" if is_dir else "-" + mode = int(meta.permissions, 8) if meta.permissions.isdigit() else 0o644 + for shift in (6, 3, 0): + bits = (mode >> shift) & 7 + perm_str += "r" if bits & 4 else "-" + perm_str += "w" if bits & 2 else "-" + perm_str += "x" if bits & 1 else "-" + size = meta.size if not is_dir else 4096 + return f"{perm_str} 1 {meta.owner} {meta.group} {size:>8} Jan 15 10:30 {name}{'/' if is_dir else ''}" + + def _dir_format(self, fs, mp, entries, truncated, total_count): + """Windows dir format.""" + dir_display = _machine_stub_display(mp) + lines = [ + " Volume in drive C has no label.", + f" Directory of {dir_display}", + "", + ] + file_count = 0 + dir_count = 0 + total_size = 0 + for name in entries: + child = f"{mp}/{name}" if mp else name + is_dir = fs.isdir(child) + meta = fs.get_meta(child) + if is_dir: + dir_count += 1 + lines.append(f"01/15/2024 10:30 AM {name}") + else: + file_count += 1 + total_size += meta.size + lines.append(f"01/15/2024 10:30 AM {meta.size:>8} {name}") + if truncated: + lines.append(f"... ({total_count - 50} more entries)") + lines.append(f" {file_count} File(s) {total_size:>10} bytes") + lines.append(f" {dir_count} Dir(s) 500,000,000 bytes free") + return "\n".join(lines) + + +def _machine_stub_display(mp): + """Quick display for dir header.""" + parts = mp.split("/") + if parts and len(parts[0]) == 1 and parts[0].isalpha(): + drive = parts[0].upper() + return f"{drive}:\\" + "\\".join(parts[1:]) + return "/" + mp + + +# --------------------------------------------------------------------------- +# cd +# --------------------------------------------------------------------------- + +class CdHandler(CommandHandler): + name = "cd" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + m = _machine(env) + if not args: + if m.os_type == OSType.WINDOWS and not env.ssh_connected: + return m.to_display_path(m.cwd) + m.cwd = m.home_dir + self._update_pwd(m) + return "" + target = args[0] + mp = _resolve(env, target) + if not m.fs.exists(mp): + if m.os_type == OSType.WINDOWS and not env.ssh_connected: + return "The system cannot find the path specified." + return f"bash: cd: {target}: No such file or directory" + if not m.fs.isdir(mp): + if m.os_type == OSType.WINDOWS and not env.ssh_connected: + return "The directory name is invalid." + return f"bash: cd: {target}: Not a directory" + m.cwd = mp + self._update_pwd(m) + if m.os_type == OSType.WINDOWS and not env.ssh_connected: + return m.to_display_path(m.cwd) + return "" + + @staticmethod + def _update_pwd(m): + display = m.to_display_path(m.cwd) + if m.os_type == OSType.WINDOWS: + m.env_vars["CD"] = display + else: + m.env_vars["PWD"] = display + + +# --------------------------------------------------------------------------- +# pwd +# --------------------------------------------------------------------------- + +class PwdHandler(CommandHandler): + name = "pwd" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + return _machine(env).to_display_path(_machine(env).cwd) + + +# --------------------------------------------------------------------------- +# cat +# --------------------------------------------------------------------------- + +class CatHandler(CommandHandler): + name = "cat" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if not args: + return "cat: missing operand" + m = _machine(env) + outputs = [] + for f in args: + mp = _resolve(env, f) + if not m.fs.exists(mp): + outputs.append(f"cat: {f}: No such file or directory") + elif m.fs.isdir(mp): + outputs.append(f"cat: {f}: Is a directory") + else: + meta = m.fs.get_meta(mp) + if meta.archive_type: + outputs.append(f"[Binary file - {meta.archive_type} archive, " + f"{len(meta.archive_entries or {})} entries]") + else: + outputs.append(m.fs.readtext(mp)) + return "\n".join(outputs) + + +# --------------------------------------------------------------------------- +# mkdir +# --------------------------------------------------------------------------- + +class MkdirHandler(CommandHandler): + name = "mkdir" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + m = _machine(env) + create_parents = False + paths = [] + for a in args: + if a in ("-p", "--parents"): + create_parents = True + else: + paths.append(a) + if not paths: + return "mkdir: missing operand" + outputs = [] + for p in paths: + mp = _resolve(env, p) + try: + if create_parents: + m.fs.makedirs(mp) + else: + m.fs.mkdir(mp) + except FileExistsError: + outputs.append(f"mkdir: cannot create directory '{p}': File exists") + except FileNotFoundError: + outputs.append(f"mkdir: cannot create directory '{p}': No such file or directory") + except NotADirectoryError: + outputs.append(f"mkdir: cannot create directory '{p}': Not a directory") + return "\n".join(outputs) + + +# --------------------------------------------------------------------------- +# chmod +# --------------------------------------------------------------------------- + +class ChmodHandler(CommandHandler): + name = "chmod" + available_os = {OSType.LINUX, OSType.MAC} + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if len(args) < 2: + return "chmod: missing operand" + recursive = False + mode_str = None + paths = [] + for a in args: + if a in ("-R", "--recursive"): + recursive = True + elif mode_str is None and len(a) == 3 and all(c in "01234567" for c in a): + mode_str = a + else: + paths.append(a) + if mode_str is None: + return f"chmod: invalid mode: '{args[0]}'" + if not paths: + return "chmod: missing operand" + + m = _machine(env) + outputs = [] + for p in paths: + mp = _resolve(env, p) + if not m.fs.exists(mp): + outputs.append(f"chmod: cannot access '{p}': No such file or directory") + continue + if recursive and m.fs.isdir(mp): + for dirpath, dirs, files in m.fs.walk(mp): + for name in dirs + files: + child = f"{dirpath}/{name}" if dirpath else name + meta = m.fs.get_meta(child) + meta.permissions = mode_str + else: + meta = m.fs.get_meta(mp) + meta.permissions = mode_str + return "\n".join(outputs) + + +# --------------------------------------------------------------------------- +# cp +# --------------------------------------------------------------------------- + +class CpHandler(CommandHandler): + name = "cp" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + recursive = False + paths = [] + for a in args: + if a in ("-r", "-R", "--recursive"): + recursive = True + else: + paths.append(a) + if len(paths) < 2: + return "cp: missing destination operand" + + m = _machine(env) + src = _resolve(env, paths[0]) + dst = _resolve(env, paths[1]) + + if not m.fs.exists(src): + return f"cp: cannot stat '{paths[0]}': No such file or directory" + if m.fs.isdir(src) and not recursive: + return f"cp: -r not specified; omitting directory '{paths[0]}'" + + # If dst is an existing dir, copy into it + if m.fs.isdir(dst): + name = src.split("/")[-1] + dst = f"{dst}/{name}" + + try: + if m.fs.isdir(src): + m.fs.copy_tree(src, dst) + else: + # Ensure parent exists + parent = "/".join(dst.split("/")[:-1]) + if parent and not m.fs.exists(parent): + return f"cp: cannot create regular file '{paths[1]}': No such file or directory" + m.fs.copy_file(src, dst) + except Exception as e: + return f"cp: {e}" + return "" + + +# --------------------------------------------------------------------------- +# mv +# --------------------------------------------------------------------------- + +class MvHandler(CommandHandler): + name = "mv" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + paths = [a for a in args if not a.startswith("-")] + if len(paths) < 2: + return "mv: missing destination operand" + + m = _machine(env) + src = _resolve(env, paths[0]) + dst = _resolve(env, paths[1]) + + if not m.fs.exists(src): + return f"mv: cannot stat '{paths[0]}': No such file or directory" + + # If dst is existing dir, move into it + if m.fs.isdir(dst): + name = src.split("/")[-1] + dst = f"{dst}/{name}" + + # Check parent of dst + dst_parent = "/".join(dst.split("/")[:-1]) + if dst_parent and not m.fs.isdir(dst_parent): + return f"mv: cannot move '{paths[0]}' to '{paths[1]}': No such file or directory" + + try: + if m.fs.isdir(src): + m.fs.copy_tree(src, dst) + else: + m.fs.copy_file(src, dst) + m.fs.remove_recursive(src) + except Exception as e: + return f"mv: {e}" + return "" + + +# --------------------------------------------------------------------------- +# rm +# --------------------------------------------------------------------------- + +class RmHandler(CommandHandler): + name = "rm" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + recursive = False + force = False + paths = [] + for a in args: + if a.startswith("-"): + flags = a.lstrip("-") + if "r" in flags or "R" in flags: + recursive = True + if "f" in flags: + force = True + else: + paths.append(a) + if not paths: + return "rm: missing operand" + + m = _machine(env) + outputs = [] + for p in paths: + mp = _resolve(env, p) + if not m.fs.exists(mp): + if not force: + outputs.append(f"rm: cannot remove '{p}': No such file or directory") + continue + if m.fs.isdir(mp) and not recursive: + outputs.append(f"rm: cannot remove '{p}': Is a directory") + continue + try: + if recursive: + m.fs.remove_recursive(mp) + else: + m.fs.remove(mp) + except Exception as e: + outputs.append(f"rm: {e}") + return "\n".join(outputs) + + +# --------------------------------------------------------------------------- +# touch +# --------------------------------------------------------------------------- + +class TouchHandler(CommandHandler): + name = "touch" + available_os = {OSType.LINUX, OSType.MAC} + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if not args: + return "touch: missing file operand" + m = _machine(env) + for f in args: + if f.startswith("-"): + continue + mp = _resolve(env, f) + if not m.fs.exists(mp): + parent = "/".join(mp.split("/")[:-1]) + if parent and not m.fs.isdir(parent): + return f"touch: cannot touch '{f}': No such file or directory" + m.fs.writetext(mp, "") + return "" + + +# --------------------------------------------------------------------------- +# echo +# --------------------------------------------------------------------------- + +class EchoHandler(CommandHandler): + name = "echo" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + return " ".join(args) + + +# --------------------------------------------------------------------------- +# whoami +# --------------------------------------------------------------------------- + +class WhoamiHandler(CommandHandler): + name = "whoami" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + return _machine(env).username + + +# --------------------------------------------------------------------------- +# ssh +# --------------------------------------------------------------------------- + +class SshHandler(CommandHandler): + name = "ssh" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if env.ssh_connected: + return "bash: already connected to remote host. Use 'exit' first." + + # Parse: ssh [-p port] [-i key] user@host + target = None + i = 0 + while i < len(args): + if args[i] in ("-p", "-i") and i + 1 < len(args): + i += 2 # skip flag and value + continue + if not args[i].startswith("-"): + target = args[i] + break + i += 1 + + if target is None: + return "usage: ssh [user@]hostname" + + if "@" not in target: + return f"ssh: Could not resolve hostname {target}: Name or service not known" + + user, host = target.split("@", 1) + # Check against known remote + if host != env.remote.hostname and host != env.remote.env_vars.get("IP", ""): + return f"ssh: connect to host {host} port 22: Connection refused" + if user != env.remote.username: + return f"Permission denied (publickey,password)." + + env.ssh_connected = True + env.current_machine = env.remote + return f"Welcome to Ubuntu 22.04 LTS ({env.remote.hostname})\nLast login: Mon Jan 15 10:30:00 2024" + + +# --------------------------------------------------------------------------- +# exit +# --------------------------------------------------------------------------- + +class ExitHandler(CommandHandler): + name = "exit" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if env.ssh_connected: + host = env.remote.hostname + env.ssh_connected = False + env.current_machine = env.local + return f"Connection to {host} closed." + return "" + + +# --------------------------------------------------------------------------- +# scp +# --------------------------------------------------------------------------- + +class ScpHandler(CommandHandler): + name = "scp" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if env.ssh_connected: + return "bash: scp must be run from local machine. Use 'exit' to disconnect first." + + recursive = False + positional = [] + i = 0 + while i < len(args): + a = args[i] + if a in ("-r", "-R"): + recursive = True + elif a in ("-P", "-i") and i + 1 < len(args): + i += 1 # skip value + elif not a.startswith("-"): + positional.append(a) + i += 1 + + if len(positional) < 2: + return "usage: scp [-r] source ... target" + + src_arg = positional[0] + dst_arg = positional[1] + + src_user, src_host, src_path = _parse_scp_target(src_arg) + dst_user, dst_host, dst_path = _parse_scp_target(dst_arg) + + # Determine source/dest machines + if src_host and dst_host: + return "scp: copying between two remote hosts is not supported" + elif src_host: + # Download: remote -> local + src_machine = env.remote + dst_machine = env.local + if src_host != env.remote.hostname and src_host != env.remote.env_vars.get("IP", ""): + return f"ssh: connect to host {src_host} port 22: Connection refused" + elif dst_host: + # Upload: local -> remote + src_machine = env.local + dst_machine = env.remote + if dst_host != env.remote.hostname and dst_host != env.remote.env_vars.get("IP", ""): + return f"ssh: connect to host {dst_host} port 22: Connection refused" + else: + return "scp: use cp for local-to-local copy" + + src_mp = src_machine.resolve_path(src_path) + dst_mp = dst_machine.resolve_path(dst_path) + + if not src_machine.fs.exists(src_mp): + return f"scp: {src_path}: No such file or directory" + + if src_machine.fs.isdir(src_mp) and not recursive: + return f"scp: {src_path}: not a regular file" + + # If dst is existing dir, copy into it + if dst_machine.fs.isdir(dst_mp): + name = src_mp.split("/")[-1] + dst_mp = f"{dst_mp}/{name}" + + # Check dst parent exists + dst_parent = "/".join(dst_mp.split("/")[:-1]) + if dst_parent and not dst_machine.fs.isdir(dst_parent): + return f"scp: {dst_path}: No such file or directory" + + try: + self._cross_copy(src_machine, src_mp, dst_machine, dst_mp, recursive) + except Exception as e: + return f"scp: {e}" + + filename = src_mp.split("/")[-1] + size = src_machine.fs.get_meta(src_mp).size + return f"{filename} 100% {_format_size(size)} transferred" + + def _cross_copy(self, src_m, src_mp, dst_m, dst_mp, recursive): + """Copy across two different MachineStates.""" + src_fs = src_m.fs + dst_fs = dst_m.fs + if src_fs.isfile(src_mp): + content = src_fs.readtext(src_mp) + dst_fs.writetext(dst_mp, content, create_parents=False) + src_meta = src_fs.get_meta(src_mp) + dst_meta = dst_fs.get_meta(dst_mp) + dst_meta.permissions = src_meta.permissions + dst_meta.owner = dst_m.username + dst_meta.group = dst_m.username + dst_meta.archive_type = src_meta.archive_type + dst_meta.archive_entries = ( + dict(src_meta.archive_entries) if src_meta.archive_entries else None + ) + elif recursive: + dst_fs.makedirs(dst_mp) + for name in src_fs.listdir(src_mp): + s = f"{src_mp}/{name}" + d = f"{dst_mp}/{name}" + self._cross_copy(src_m, s, dst_m, d, True) + + +# --------------------------------------------------------------------------- +# rsync +# --------------------------------------------------------------------------- + +class RsyncHandler(CommandHandler): + name = "rsync" + available_os = {OSType.LINUX, OSType.MAC} + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if env.ssh_connected: + return "bash: rsync must be run from local machine. Use 'exit' to disconnect first." + + # Strip flags, find positional args + positional = [] + i = 0 + while i < len(args): + a = args[i] + if a in ("-e",) and i + 1 < len(args): + i += 1 # skip value + elif not a.startswith("-"): + positional.append(a) + i += 1 + + if len(positional) < 2: + return "usage: rsync [options] source destination" + + src_arg = positional[0] + dst_arg = positional[1] + + # Reuse SCP logic for cross-machine copy + scp = ScpHandler() + # Build equivalent scp args + scp_args = ["-r", src_arg, dst_arg] + result = scp.execute(scp_args, env) + + if "transferred" in result: + filename = src_arg.rstrip("/").split("/")[-1] + return (f"sending incremental file list\n" + f"{filename}\n" + f"\nsent 1024 bytes received 42 bytes 2132.00 bytes/sec\n" + f"total size is 1024 speedup is 0.96") + return result + + +# --------------------------------------------------------------------------- +# tar +# --------------------------------------------------------------------------- + +class TarHandler(CommandHandler): + name = "tar" + available_os = {OSType.LINUX, OSType.MAC} + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if not args: + return "tar: You must specify one of the '-Acdtrux' options" + + m = _machine(env) + fs = m.fs + + # Collect all flags and positional args. + # tar accepts flags in many forms: -czf, czf, -c -z -f, --create, etc. + mode = None + gzip = False + archive_path = None + file_args = [] + expect_archive = False # next positional arg is the archive path + + for a in args: + if expect_archive: + archive_path = a + expect_archive = False + continue + if a.startswith("--"): + if a == "--create": + mode = "create" + elif a in ("--extract", "--get"): + mode = "extract" + elif a == "--list": + mode = "list" + elif a in ("--gzip", "--gunzip", "--ungzip"): + gzip = True + elif a.startswith("--file="): + archive_path = a.split("=", 1)[1] + elif a == "--file": + expect_archive = True + # --verbose and other long flags are silently ignored + continue + if a.startswith("-") or (a == args[0] and len(a) <= 6 and a[0] in "cxtzvf"): + # Short flags: -czf, czf, -c, -z, -f + chars = a.lstrip("-") + for ch in chars: + if ch == "c": + mode = "create" + elif ch == "x": + mode = "extract" + elif ch == "t": + mode = "list" + elif ch == "z": + gzip = True + elif ch == "f": + expect_archive = True + # v and other single-char flags silently ignored + continue + # Positional arg + file_args.append(a) + + if archive_path is None: + return "tar: Refusing to read archive contents from terminal" + + archive_mp = _resolve(env, archive_path) + archive_type = "tar.gz" if gzip else "tar" + + if mode == "create": + if not file_args: + return "tar: Cowardly refusing to create an empty archive" + entries = {} + for f in file_args: + fmp = _resolve(env, f) + if not fs.exists(fmp): + return f"tar: {f}: Cannot open: No such file or directory" + if fs.isfile(fmp): + basename = fmp.split("/")[-1] + entries[basename] = fs.readtext(fmp) + elif fs.isdir(fmp): + base = fmp.rstrip("/").split("/")[-1] + for dp, dirs, files in fs.walk(fmp): + for fname in files: + child = f"{dp}/{fname}" if dp else fname + rel = child[len(fmp):].lstrip("/") if child.startswith(fmp) else child + full_rel = f"{base}/{rel}" if rel else base + entries[full_rel] = fs.readtext(child) + + content = json.dumps({"type": archive_type, "entries": entries}) + parent = "/".join(archive_mp.split("/")[:-1]) + if parent and not fs.isdir(parent): + return f"tar: {archive_path}: Cannot open: No such file or directory" + fs.writetext(archive_mp, content) + meta = fs.get_meta(archive_mp) + meta.archive_type = archive_type + meta.archive_entries = entries + meta.size = sum(len(v) for v in entries.values()) + return "" + + elif mode == "extract": + if not fs.exists(archive_mp): + return f"tar: {archive_path}: Cannot open: No such file or directory" + meta = fs.get_meta(archive_mp) + if not meta.archive_type or "tar" not in meta.archive_type: + return f"tar: {archive_path}: This does not look like a tar archive" + entries = meta.archive_entries or {} + for rel_path, content in entries.items(): + out_path = _resolve(env, rel_path) + parent = "/".join(out_path.split("/")[:-1]) + if parent: + fs.makedirs(parent) + fs.writetext(out_path, content) + out_meta = fs.get_meta(out_path) + out_meta.owner = m.username + out_meta.group = m.username + return "" + + elif mode == "list": + if not fs.exists(archive_mp): + return f"tar: {archive_path}: Cannot open: No such file or directory" + meta = fs.get_meta(archive_mp) + if not meta.archive_type or "tar" not in meta.archive_type: + return f"tar: {archive_path}: This does not look like a tar archive" + entries = meta.archive_entries or {} + return "\n".join(sorted(entries.keys())) + + return "tar: You must specify one of the '-Acdtrux' options" + + +# --------------------------------------------------------------------------- +# zip +# --------------------------------------------------------------------------- + +class ZipHandler(CommandHandler): + name = "zip" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if len(args) < 2: + return "zip: missing archive name or files" + + m = _machine(env) + fs = m.fs + recursive = False + positional = [] + for a in args: + if a in ("-r", "-R"): + recursive = True + elif not a.startswith("-"): + positional.append(a) + + if len(positional) < 2: + return "zip: missing archive name or files" + + archive_name = positional[0] + file_args = positional[1:] + archive_mp = _resolve(env, archive_name) + + entries = {} + output_lines = [] + for f in file_args: + fmp = _resolve(env, f) + if not fs.exists(fmp): + return f"zip error: Nothing to do! ({f} not found)" + if fs.isfile(fmp): + basename = fmp.split("/")[-1] + entries[basename] = fs.readtext(fmp) + output_lines.append(f" adding: {basename} (stored 0%)") + elif fs.isdir(fmp): + if not recursive: + output_lines.append(f" adding: {f}/ (stored 0%)") + continue + base = fmp.rstrip("/").split("/")[-1] + for dp, dirs, files in fs.walk(fmp): + for fname in files: + child = f"{dp}/{fname}" if dp else fname + rel = child[len(fmp):].lstrip("/") if child.startswith(fmp) else child + full_rel = f"{base}/{rel}" if rel else base + entries[full_rel] = fs.readtext(child) + output_lines.append(f" adding: {full_rel} (stored 0%)") + + content = json.dumps({"type": "zip", "entries": entries}) + parent = "/".join(archive_mp.split("/")[:-1]) + if parent and not fs.isdir(parent): + fs.makedirs(parent) + fs.writetext(archive_mp, content) + meta = fs.get_meta(archive_mp) + meta.archive_type = "zip" + meta.archive_entries = entries + meta.size = sum(len(v) for v in entries.values()) + + return "\n".join(output_lines) + + +# --------------------------------------------------------------------------- +# unzip +# --------------------------------------------------------------------------- + +class UnzipHandler(CommandHandler): + name = "unzip" + + def execute(self, args: List[str], env: TerminalEnv) -> str: + if not args: + return "unzip: missing archive name" + + m = _machine(env) + fs = m.fs + archive_name = None + dest_dir = None + i = 0 + while i < len(args): + if args[i] == "-d" and i + 1 < len(args): + dest_dir = args[i + 1] + i += 2 + continue + if not args[i].startswith("-"): + archive_name = args[i] + i += 1 + + if archive_name is None: + return "unzip: missing archive name" + + archive_mp = _resolve(env, archive_name) + if not fs.exists(archive_mp): + return f"unzip: cannot find or open {archive_name}" + + meta = fs.get_meta(archive_mp) + if meta.archive_type != "zip": + return f"unzip: {archive_name} is not a zip archive" + + entries = meta.archive_entries or {} + output_lines = [f"Archive: {archive_name}"] + + for rel_path, content in entries.items(): + if dest_dir: + out_path = _resolve(env, f"{dest_dir}/{rel_path}") + else: + out_path = _resolve(env, rel_path) + parent = "/".join(out_path.split("/")[:-1]) + if parent: + fs.makedirs(parent) + fs.writetext(out_path, content) + out_meta = fs.get_meta(out_path) + out_meta.owner = m.username + out_meta.group = m.username + output_lines.append(f" extracting: {rel_path}") + + return "\n".join(output_lines) + + +# --------------------------------------------------------------------------- +# gzip / gunzip +# --------------------------------------------------------------------------- + +class GzipHandler(CommandHandler): + name = "gzip" + available_os = {OSType.LINUX, OSType.MAC} + + def execute(self, args: List[str], env: TerminalEnv) -> str: + paths = [a for a in args if not a.startswith("-")] + if not paths: + return "gzip: missing file operand" + + m = _machine(env) + fs = m.fs + for f in paths: + mp = _resolve(env, f) + if not fs.exists(mp): + return f"gzip: {f}: No such file or directory" + if fs.isdir(mp): + return f"gzip: {f}: Is a directory" + content = fs.readtext(mp) + gz_path = mp + ".gz" + gz_content = json.dumps({"type": "gzip", "entries": {f: content}}) + fs.writetext(gz_path, gz_content) + gz_meta = fs.get_meta(gz_path) + gz_meta.archive_type = "gzip" + gz_meta.archive_entries = {f: content} + gz_meta.size = len(content) + fs.remove(mp) + return "" + + +class GunzipHandler(CommandHandler): + name = "gunzip" + available_os = {OSType.LINUX, OSType.MAC} + + def execute(self, args: List[str], env: TerminalEnv) -> str: + paths = [a for a in args if not a.startswith("-")] + if not paths: + return "gunzip: missing file operand" + + m = _machine(env) + fs = m.fs + for f in paths: + mp = _resolve(env, f) + if not fs.exists(mp): + return f"gunzip: {f}: No such file or directory" + meta = fs.get_meta(mp) + if meta.archive_type != "gzip": + return f"gunzip: {f}: not in gzip format" + entries = meta.archive_entries or {} + for orig_name, content in entries.items(): + out_path = _resolve(env, orig_name) + fs.writetext(out_path, content) + fs.remove(mp) + return "" + + +# --------------------------------------------------------------------------- +# Registry +# --------------------------------------------------------------------------- + +def build_command_registry() -> Dict[str, CommandHandler]: + """Build the command name -> handler mapping.""" + handlers = [ + LsHandler(), + CdHandler(), + PwdHandler(), + CatHandler(), + MkdirHandler(), + ChmodHandler(), + CpHandler(), + MvHandler(), + RmHandler(), + TouchHandler(), + EchoHandler(), + WhoamiHandler(), + SshHandler(), + ExitHandler(), + ScpHandler(), + RsyncHandler(), + TarHandler(), + ZipHandler(), + UnzipHandler(), + GzipHandler(), + GunzipHandler(), + ] + registry = {} + for h in handlers: + registry[h.name] = h + return registry diff --git a/trinity/common/workflows/connect_the_dots/terminal/env.py b/trinity/common/workflows/connect_the_dots/terminal/env.py new file mode 100644 index 00000000000..6ffbfc2ca85 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/terminal/env.py @@ -0,0 +1,636 @@ +# -*- coding: utf-8 -*- +""" +Virtual terminal environment with in-memory filesystem. + +No real commands are executed. All state is held in Python dicts. +""" + +import re +import shlex +from dataclasses import dataclass, field +from enum import Enum +from typing import Any, Dict, List, Optional, Tuple + + +# --------------------------------------------------------------------------- +# Enums +# --------------------------------------------------------------------------- + +class OSType(Enum): + WINDOWS = "windows" + MAC = "mac" + LINUX = "linux" + + +# --------------------------------------------------------------------------- +# Virtual Filesystem +# --------------------------------------------------------------------------- + + + +@dataclass +class FileMeta: + """Metadata attached to every file/directory.""" + permissions: str = "644" + owner: str = "user" + group: str = "user" + size: int = 0 + # For simulated archives + archive_type: Optional[str] = None # "tar", "zip", "tar.gz", "gzip" + archive_entries: Optional[Dict[str, str]] = None + + +class VirtualFS: + """Minimal in-memory filesystem backed by nested dicts. + + Internal layout:: + + _tree = { + "home": { + "user": { + "file.txt": "content string", + "subdir": { ... }, + } + } + } + + - A *str* value => regular file (the string is its content). + - A *dict* value => directory. + + All paths inside VirtualFS use ``/`` separators with no leading slash. + The caller (MachineState) is responsible for converting OS-specific paths + to this canonical form *before* calling VirtualFS methods. + """ + + def __init__(self): + self._tree: Dict[str, Any] = {} + self._meta: Dict[str, FileMeta] = {} # canonical_path -> metadata + + # -- helpers -------------------------------------------------------- + + @staticmethod + def _split(path: str) -> List[str]: + """Split a canonical path into parts, filtering blanks.""" + return [p for p in path.replace("\\", "/").split("/") if p] + + def _navigate(self, parts: List[str], create_parents: bool = False): + """Walk *parts* from root. Returns (parent_dict, last_key). + + If *create_parents* is True, intermediate dirs are created. + Raises FileNotFoundError if a segment doesn't exist (and create is off). + Raises NotADirectoryError if a segment is a file, not a dir. + """ + node = self._tree + for i, part in enumerate(parts[:-1]): + child = node.get(part) + if child is None: + if create_parents: + node[part] = {} + dir_path = "/".join(parts[: i + 1]) + self._meta[dir_path] = FileMeta(permissions="755", size=4096) + node = node[part] + else: + raise FileNotFoundError("/".join(parts[: i + 1])) + elif isinstance(child, dict): + node = child + else: + raise NotADirectoryError("/".join(parts[: i + 1])) + return (node, parts[-1]) if parts else (self._tree, "") + + # -- public API ----------------------------------------------------- + + def exists(self, path: str) -> bool: + parts = self._split(path) + if not parts: + return True # root always exists + try: + parent, key = self._navigate(parts) + return key in parent + except (FileNotFoundError, NotADirectoryError): + return False + + def isdir(self, path: str) -> bool: + parts = self._split(path) + if not parts: + return True + try: + parent, key = self._navigate(parts) + return isinstance(parent.get(key), dict) + except (FileNotFoundError, NotADirectoryError): + return False + + def isfile(self, path: str) -> bool: + parts = self._split(path) + if not parts: + return False + try: + parent, key = self._navigate(parts) + val = parent.get(key) + return val is not None and not isinstance(val, dict) + except (FileNotFoundError, NotADirectoryError): + return False + + def listdir(self, path: str) -> List[str]: + parts = self._split(path) + node = self._tree + for part in parts: + child = node.get(part) + if child is None: + raise FileNotFoundError(path) + if not isinstance(child, dict): + raise NotADirectoryError(path) + node = child + return sorted(node.keys()) + + def readtext(self, path: str) -> str: + parts = self._split(path) + if not parts: + raise IsADirectoryError(path) + parent, key = self._navigate(parts) + val = parent.get(key) + if val is None: + raise FileNotFoundError(path) + if isinstance(val, dict): + raise IsADirectoryError(path) + return val + + def writetext(self, path: str, content: str, create_parents: bool = False) -> None: + parts = self._split(path) + if not parts: + raise IsADirectoryError(path) + parent, key = self._navigate(parts, create_parents=create_parents) + parent[key] = content + meta = self._meta.get(path) + if meta is None: + meta = FileMeta() + self._meta[path] = meta + meta.size = len(content) + + def makedirs(self, path: str) -> None: + parts = self._split(path) + if not parts: + return + node = self._tree + for i, part in enumerate(parts): + child = node.get(part) + if child is None: + node[part] = {} + dir_path = "/".join(parts[: i + 1]) + self._meta[dir_path] = FileMeta(permissions="755", size=4096) + node = node[part] + elif isinstance(child, dict): + node = child + else: + raise NotADirectoryError("/".join(parts[: i + 1])) + + def mkdir(self, path: str) -> None: + """Create a single directory (parent must exist).""" + parts = self._split(path) + if not parts: + return + parent, key = self._navigate(parts) + if key in parent: + raise FileExistsError(path) + parent[key] = {} + self._meta[path] = FileMeta(permissions="755", size=4096) + + def remove(self, path: str) -> None: + parts = self._split(path) + if not parts: + raise PermissionError("cannot remove root") + parent, key = self._navigate(parts) + if key not in parent: + raise FileNotFoundError(path) + val = parent[key] + if isinstance(val, dict) and val: + raise OSError(f"directory not empty: {path}") + del parent[key] + self._meta.pop(path, None) + + def remove_recursive(self, path: str) -> None: + parts = self._split(path) + if not parts: + self._tree.clear() + self._meta.clear() + return + parent, key = self._navigate(parts) + if key not in parent: + raise FileNotFoundError(path) + # Remove all metadata under this path + prefix = path.rstrip("/") + "/" + to_del = [k for k in self._meta if k == path or k.startswith(prefix)] + for k in to_del: + del self._meta[k] + del parent[key] + + def get_meta(self, path: str) -> FileMeta: + parts = self._split(path) + canon = "/".join(parts) if parts else "" + meta = self._meta.get(canon) + if meta is None: + # Auto-create metadata + meta = FileMeta() + if self.isdir(path): + meta.permissions = "755" + meta.size = 4096 + else: + try: + content = self.readtext(path) + meta.size = len(content) + except Exception: + pass + self._meta[canon] = meta + return meta + + + def walk(self, path: str = "") -> List[Tuple[str, List[str], List[str]]]: + """Walk filesystem tree. Yields (dir_path, [subdirs], [files]).""" + parts = self._split(path) + node = self._tree + for part in parts: + child = node.get(part) + if child is None or not isinstance(child, dict): + return [] + node = child + + result = [] + self._walk_recursive(node, path, result) + return result + + def _walk_recursive(self, node: dict, prefix: str, result: list): + dirs = [] + files = [] + for name, val in sorted(node.items()): + if isinstance(val, dict): + dirs.append(name) + else: + files.append(name) + result.append((prefix, dirs, files)) + for d in dirs: + child_prefix = f"{prefix}/{d}" if prefix else d + self._walk_recursive(node[d], child_prefix, result) + + def copy_file(self, src: str, dst: str) -> None: + """Copy a single file.""" + content = self.readtext(src) + src_meta = self.get_meta(src) + self.writetext(dst, content, create_parents=False) + dst_meta = self.get_meta(dst) + dst_meta.permissions = src_meta.permissions + dst_meta.owner = src_meta.owner + dst_meta.group = src_meta.group + dst_meta.archive_type = src_meta.archive_type + dst_meta.archive_entries = ( + dict(src_meta.archive_entries) if src_meta.archive_entries else None + ) + + def copy_tree(self, src: str, dst: str) -> None: + """Recursively copy directory.""" + if self.isfile(src): + self.copy_file(src, dst) + return + self.makedirs(dst) + for name in self.listdir(src): + s = f"{src}/{name}" if src else name + d = f"{dst}/{name}" if dst else name + if self.isdir(s): + self.copy_tree(s, d) + else: + self.copy_file(s, d) + + +# --------------------------------------------------------------------------- +# Machine State +# --------------------------------------------------------------------------- + +@dataclass +class MachineState: + """State of a single virtual machine.""" + os_type: OSType + hostname: str + username: str + home_dir: str # canonical memfs path, e.g. "home/user" or "C/Users/user" + cwd: str # canonical memfs path + fs: VirtualFS = field(default_factory=VirtualFS) + env_vars: Dict[str, str] = field(default_factory=dict) + + # -- path conversion ------------------------------------------------ + + def to_memfs_path(self, user_path: str) -> str: + """Convert a user-visible path to canonical MemFS path (no leading /).""" + if self.os_type == OSType.WINDOWS: + return self._win_to_memfs(user_path) + return self._unix_to_memfs(user_path) + + def to_display_path(self, memfs_path: str) -> str: + """Convert canonical MemFS path to user-visible path.""" + if self.os_type == OSType.WINDOWS: + return self._memfs_to_win(memfs_path) + return "/" + memfs_path if memfs_path else "/" + + def _win_to_memfs(self, p: str) -> str: + p = p.replace("/", "\\") + # Absolute: C:\... -> C/... + if len(p) >= 2 and p[1] == ":": + drive = p[0].upper() + rest = p[2:].lstrip("\\") + parts = [drive] + [x for x in rest.split("\\") if x] + return "/".join(parts) + # Relative + parts = [x for x in p.split("\\") if x] + if not parts: + return self.cwd + return self.cwd + "/" + "/".join(parts) + + def _memfs_to_win(self, p: str) -> str: + parts = p.split("/") + if parts and len(parts[0]) == 1 and parts[0].isalpha(): + drive = parts[0].upper() + rest = "\\".join(parts[1:]) + return f"{drive}:\\{rest}" if rest else f"{drive}:\\" + return "\\".join(parts) + + def _unix_to_memfs(self, p: str) -> str: + if p.startswith("/"): + return p.lstrip("/") + # Relative + if not p or p == ".": + return self.cwd + if p == "..": + parts = self.cwd.split("/") + return "/".join(parts[:-1]) if len(parts) > 1 else "" + return f"{self.cwd}/{p}" if self.cwd else p + + def resolve_path(self, user_path: str) -> str: + """Resolve a user-visible path to canonical MemFS path, handling . and ..""" + raw = self.to_memfs_path(user_path) + parts = raw.split("/") + resolved = [] + for p in parts: + if p == "" or p == ".": + continue + elif p == "..": + if resolved: + resolved.pop() + else: + resolved.append(p) + return "/".join(resolved) + + def get_display_cwd(self) -> str: + """CWD for display in terminal prompt, with ~ substitution (Unix only).""" + display = self.to_display_path(self.cwd) + if self.os_type == OSType.WINDOWS: + return display + home_display = self.to_display_path(self.home_dir) + if display == home_display: + return "~" + if display.startswith(home_display + "/"): + return "~" + display[len(home_display):] + return display + + def get_prompt_string(self) -> str: + """Render the terminal prompt.""" + if self.os_type == OSType.WINDOWS: + return f"{self.to_display_path(self.cwd)}>" + elif self.os_type == OSType.MAC: + return f"{self.username}@MacBook {self.get_display_cwd()} % " + else: + return f"{self.username}@{self.hostname}:{self.get_display_cwd()}$ " + + +# --------------------------------------------------------------------------- +# Terminal Environment +# --------------------------------------------------------------------------- + +class TerminalEnv: + """Complete terminal environment with local and remote machines. + + The *command_handlers* dict is injected by the caller (see commands.py). + """ + + def __init__( + self, + local: MachineState, + remote: MachineState, + command_handlers: Dict[str, Any], + max_steps: int = 15, + ): + self.local = local + self.remote = remote + self.command_handlers = command_handlers + self.max_steps = max_steps + self.current_machine: MachineState = self.local + self.ssh_connected: bool = False + self.step_count: int = 0 + self.command_history: List[str] = [] + + # -- prompt ---------------------------------------------------------- + + def get_prompt_string(self) -> str: + return self.current_machine.get_prompt_string() + + # -- execution ------------------------------------------------------- + + def step(self, raw_command: str, goal_check_fn=None): + """Gym-style step. Returns (observation, reward, done, info). + + Args: + raw_command: The command string to execute. + goal_check_fn: Optional callable ``fn(env) -> float`` that returns + 1.0 if the task is complete, 0.0 otherwise. When it returns + 1.0, the episode terminates early. + """ + observation = self.execute(raw_command) + reward = 0.0 + done = False + info = {} + + if goal_check_fn is not None: + reward = goal_check_fn(self) + if reward > 0.5: + done = True + info["early_completion"] = True + + if self.step_count >= self.max_steps: + done = True + + return observation, reward, done, info + + def execute(self, raw_command: str) -> str: + """Execute a raw command string. Returns terminal output.""" + raw_command = raw_command.strip() + if not raw_command: + return "" + + self.command_history.append(raw_command) + self.step_count += 1 + + # Expand environment variables + expanded = self._expand_env_vars(raw_command) + + # Check for unsupported operators + if _contains_unquoted(expanded, "|"): + return "Error: pipes (|) are not supported in this environment." + if _contains_unquoted(expanded, ">") or _contains_unquoted(expanded, "<"): + return "Error: redirects (>, >>, <) are not supported in this environment." + + # Split on && and ; + segments = _split_command_chain(expanded) + + output_parts = [] + last_was_error = False + prev_operator = None + for cmd_str, operator in segments: + cmd_str = cmd_str.strip() + if not cmd_str: + prev_operator = operator + continue + # && : skip if previous command failed + if prev_operator == "&&" and last_was_error: + break + result, last_was_error = self._execute_single(cmd_str) + if result: + output_parts.append(result) + prev_operator = operator + + return "\n".join(output_parts) + + def _execute_single(self, cmd_str: str) -> Tuple[str, bool]: + """Execute one command. Returns (output, is_error).""" + try: + posix = self.current_machine.os_type != OSType.WINDOWS or self.ssh_connected + tokens = shlex.split(cmd_str, posix=posix) + except ValueError: + return "bash: syntax error: unexpected end of file", True + + if not tokens: + return "", False + + cmd_name = tokens[0] + args = tokens[1:] + + # Resolve aliases + cmd_key = self._resolve_alias(cmd_name.lower()) + + handler = self.command_handlers.get(cmd_key) + if handler is None: + return self._unknown_cmd_error(cmd_name), True + + # Check OS availability + if not handler.is_available(self.current_machine.os_type, self.ssh_connected): + return self._unknown_cmd_error(cmd_name), True + + try: + result = handler.execute(args, self) + # Detect error by common patterns: "cmd: error...", "Error:", "bash:", + # Windows errors, or empty success + is_err = bool(result) and ( + result.startswith(("Error:", "bash:", "'", "The system", "Could Not")) + or (": " in result.split("\n")[0] + and result.split(":")[0].split()[-1].lower() in ( + "ls", "cd", "cat", "mkdir", "chmod", "cp", "mv", "rm", + "scp", "rsync", "tar", "zip", "unzip", "gzip", "gunzip", + "ssh", "touch", "del", + )) + ) + return result, is_err + except Exception as e: + return f"Error: {e}", True + + def _expand_env_vars(self, cmd: str) -> str: + machine = self.current_machine + if machine.os_type == OSType.WINDOWS and not self.ssh_connected: + for var, val in machine.env_vars.items(): + cmd = cmd.replace(f"%{var}%", val) + else: + # ~ expansion: only standalone ~ or ~/... at token boundaries + # Do NOT expand ~ after : (e.g., scp file user@host:~/dir) + home = machine.env_vars.get("HOME", machine.to_display_path(machine.home_dir)) + cmd = re.sub(r"(? str: + aliases = { + "dir": "ls", + "type": "cat", + "copy": "cp", + "move": "mv", + "del": "rm", + "ren": "mv", + "rename": "mv", + } + return aliases.get(name, name) + + def _unknown_cmd_error(self, cmd_name: str) -> str: + if self.current_machine.os_type == OSType.WINDOWS and not self.ssh_connected: + return ( + f"'{cmd_name}' is not recognized as an internal or external command,\n" + f"operable program or batch file." + ) + return f"bash: {cmd_name}: command not found" + + +# --------------------------------------------------------------------------- +# Helpers for command chain parsing +# --------------------------------------------------------------------------- + +def _contains_unquoted(text: str, char: str) -> bool: + """Check if *char* appears outside of quotes.""" + in_single = False + in_double = False + i = 0 + while i < len(text): + c = text[i] + if c == "'" and not in_double: + in_single = not in_single + elif c == '"' and not in_single: + in_double = not in_double + elif c == char and not in_single and not in_double: + # For &&, check double char + if char == "&" and i + 1 < len(text) and text[i + 1] == "&": + return False # && is handled separately + if char in (">", "<", "|"): + return True + i += 1 + return False + + +def _split_command_chain(text: str) -> List[Tuple[str, Optional[str]]]: + """Split on ``&&`` and ``;``. Returns [(cmd, operator), ...].""" + result = [] + current = [] + in_single = False + in_double = False + i = 0 + while i < len(text): + c = text[i] + if c == "'" and not in_double: + in_single = not in_single + current.append(c) + elif c == '"' and not in_single: + in_double = not in_double + current.append(c) + elif not in_single and not in_double: + if c == ";" : + result.append(("".join(current), ";")) + current = [] + elif c == "&" and i + 1 < len(text) and text[i + 1] == "&": + result.append(("".join(current), "&&")) + current = [] + i += 1 # skip second & + else: + current.append(c) + else: + current.append(c) + i += 1 + + remaining = "".join(current).strip() + if remaining: + result.append((remaining, None)) + return result diff --git a/trinity/common/workflows/connect_the_dots/terminal/goal_check.py b/trinity/common/workflows/connect_the_dots/terminal/goal_check.py new file mode 100644 index 00000000000..b6e781b6e17 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/terminal/goal_check.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +""" +Goal checking for terminal tasks. + +Uses a unified check-list approach: every task (single or composite) defines +a list of ``goal_checks``, each a dict like:: + + {"type": "file_exists", "machine": "remote", "path": "...", "content": "..."} + +All checks must pass for reward = 1.0; any failure gives 0.0. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List + +if TYPE_CHECKING: + from trinity.common.workflows.connect_the_dots.terminal.env import TerminalEnv + from trinity.common.workflows.connect_the_dots.terminal.task_gen import TerminalTask + + +def check_goal(task: TerminalTask, env: TerminalEnv) -> float: + """Return 1.0 if all goal checks pass, 0.0 otherwise.""" + for check in task.goal_checks: + if not _evaluate_check(check, env): + return 0.0 + return 1.0 + + +def _evaluate_check(check: Dict[str, Any], env: TerminalEnv) -> bool: + """Evaluate a single goal check.""" + machine = env.remote if check["machine"] == "remote" else env.local + fs = machine.fs + path = check["path"] + check_type = check["type"] + + if check_type == "file_exists": + if not fs.isfile(path): + return False + if "content" in check: + try: + actual = fs.readtext(path) + if actual != check["content"]: + return False + except Exception: + return False + return True + + elif check_type == "file_not_exists": + return not fs.exists(path) + + elif check_type == "dir_exists": + return fs.isdir(path) + + elif check_type == "permission_equals": + if not fs.exists(path): + return False + meta = fs.get_meta(path) + return meta.permissions == check["value"] + + elif check_type == "archive_contains": + if not fs.isfile(path): + return False + meta = fs.get_meta(path) + if meta.archive_type is None: + return False + expected_entries = check.get("entries", {}) + actual_entries = meta.archive_entries or {} + for rel_path, expected_content in expected_entries.items(): + if rel_path not in actual_entries: + return False + if actual_entries[rel_path] != expected_content: + return False + return True + + else: + raise ValueError(f"Unknown check type: {check_type}") diff --git a/trinity/common/workflows/connect_the_dots/terminal/prompts/__init__.py b/trinity/common/workflows/connect_the_dots/terminal/prompts/__init__.py new file mode 100644 index 00000000000..a2f5f124acc --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/terminal/prompts/__init__.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +"""Prompt management for Terminal workflow using Jinja2 templates.""" + +from pathlib import Path +from typing import Optional + +from jinja2 import Environment, FileSystemLoader + +PROMPTS_DIR = Path(__file__).parent + + +def get_jinja_env() -> Environment: + return Environment( + loader=FileSystemLoader(PROMPTS_DIR), + trim_blocks=True, + lstrip_blocks=True, + ) + + +def load_system_prompt(**kwargs) -> str: + env = get_jinja_env() + template = env.get_template("system.jinja2") + return template.render(**kwargs) + + +def load_user_prompt( + current_step: int, + max_steps: int, + command_output: Optional[str] = None, + terminal_prompt: Optional[str] = None, + **kwargs, +) -> str: + env = get_jinja_env() + template = env.get_template("user.jinja2") + return template.render( + current_step=current_step, + max_steps=max_steps, + command_output=command_output, + terminal_prompt=terminal_prompt, + **kwargs, + ) diff --git a/trinity/common/workflows/connect_the_dots/terminal/prompts/system.jinja2 b/trinity/common/workflows/connect_the_dots/terminal/prompts/system.jinja2 new file mode 100644 index 00000000000..1e4ec8e29e4 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/terminal/prompts/system.jinja2 @@ -0,0 +1,29 @@ +You are interacting with a simulated terminal environment. Your goal is to complete file operations by typing commands. + +## Environment +- Local machine: {{ os_name }} (type: {{ os_type }}, hostname: localhost) +- Remote server: {{ remote_host }} (Linux, SSH pre-configured) +- Remote username: {{ remote_user }} + +## Available Commands +ls, dir, cd, pwd, cat, type, mkdir, chmod, cp, copy, mv, move, rm, del, ren, touch, scp, rsync, ssh, exit, whoami, echo, tar, zip, unzip, gzip, gunzip + +Note: Not all commands are available on all systems. + +## Task +{{ task_description }} + +## Response Format +First think step by step about which command to run, then provide your command using ... tags at the end. Format your complete response as follows: +``` +[THINKING] +COMMAND +``` +where [THINKING] should be replaced with your thinking process, while COMMAND is a single shell command line to execute this turn. + +Other requirements: +- Be concise and avoid overthinking in your thinking process. +- Issue exactly ONE command per turn. You may use `&&` or `;` to chain commands within the single `` block. +- If your response cannot be parsed (missing `` tags), the episode ends immediately. +- You have {{ max_steps }} steps to complete the task. +- The `` tag must appear at the END of your response. diff --git a/trinity/common/workflows/connect_the_dots/terminal/prompts/user.jinja2 b/trinity/common/workflows/connect_the_dots/terminal/prompts/user.jinja2 new file mode 100644 index 00000000000..f2404607d6f --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/terminal/prompts/user.jinja2 @@ -0,0 +1,7 @@ +Step {{ current_step }}/{{ max_steps }} +{% if command_output %} + +{{ command_output }} +{% endif %} + +{{ terminal_prompt }} \ No newline at end of file diff --git a/trinity/common/workflows/connect_the_dots/terminal/task_gen.py b/trinity/common/workflows/connect_the_dots/terminal/task_gen.py new file mode 100644 index 00000000000..b9221d11d32 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/terminal/task_gen.py @@ -0,0 +1,996 @@ +# -*- coding: utf-8 -*- +""" +Task generation for the simulated terminal environment. + +Every task is fully deterministic given a seed: the seed controls the OS type, +task type, file names, paths, remote server details, and filesystem layout. +""" + +from __future__ import annotations + +import json +import random +from dataclasses import dataclass, field +from enum import Enum +from typing import Any, Dict, List, Optional, Tuple + +from trinity.common.workflows.connect_the_dots.terminal.env import ( + FileMeta, + MachineState, + OSType, + TerminalEnv, + VirtualFS, +) + +# --------------------------------------------------------------------------- +# Task types +# --------------------------------------------------------------------------- + + +class TaskType(Enum): + UPLOAD = "upload" + DOWNLOAD = "download" + RENAME = "rename" + MOVE = "move" + CHMOD = "chmod" + DELETE = "delete" + COPY = "copy" + PACK = "pack" + MKDIR = "mkdir" + + +class CompositeTemplate(Enum): + PACK_UPLOAD = "pack_upload" + DOWNLOAD_EXTRACT = "download_extract" + MKDIR_UPLOAD = "mkdir_upload" + UPLOAD_CHMOD = "upload_chmod" + UPLOAD_DELETE_SOURCE = "upload_delete_source" + PACK_UPLOAD_EXTRACT = "pack_upload_extract" + DOWNLOAD_RENAME = "download_rename" + BACKUP_REPLACE = "backup_replace" + + +# --------------------------------------------------------------------------- +# Pools for randomisation +# --------------------------------------------------------------------------- + +FILENAMES = [ + "report.txt", "data.csv", "image.png", "config.json", "notes.md", + "script.sh", "log.txt", "readme.txt", "database.db", "output.log", + "results.csv", "document.pdf", "settings.ini", "main.py", "index.html", + "style.css", "app.js", "server.py", "deploy.sh", "backup.sql", + "photo.jpg", "diagram.svg", "metrics.json", "requirements.txt", "Makefile", +] + +DIRNAMES = [ + "projects", "documents", "downloads", "backups", "config", + "data", "logs", "workspace", "reports", "media", + "scripts", "output", "staging", "archive", "temp", + "src", "build", "dist", "assets", "uploads", +] + +REMOTE_USERS = ["admin", "deploy", "user", "webmaster", "devops", "ops", "ubuntu"] + +PERMISSION_MODES = ["644", "755", "600", "700", "664", "640", "444", "750"] + +ARCHIVE_FORMATS = ["tar", "tar.gz", "zip"] + + +# --------------------------------------------------------------------------- +# Dataclass +# --------------------------------------------------------------------------- + + +@dataclass +class TerminalTask: + """Complete specification for one terminal task.""" + seed: int + local_os: OSType + task_category: str # "single" or "composite" + task_type: str # TaskType value or CompositeTemplate value + remote_host: str + remote_user: str + description: str # human-readable task description + # goal state as a list of checks + goal_checks: List[Dict[str, Any]] = field(default_factory=list) + # initial filesystem snapshots (for building MachineState at runtime) + local_fs_spec: List[Dict[str, Any]] = field(default_factory=list) + remote_fs_spec: List[Dict[str, Any]] = field(default_factory=list) + # extra metadata + params: Dict[str, Any] = field(default_factory=dict) + + +# --------------------------------------------------------------------------- +# Filesystem helpers +# --------------------------------------------------------------------------- + + +def _home_dir(os_type: OSType) -> str: + """Canonical memfs home directory.""" + if os_type == OSType.WINDOWS: + return "C/Users/user" + elif os_type == OSType.MAC: + return "Users/user" + return "home/user" + + +def _display_path(memfs_path: str, os_type: OSType) -> str: + """Convert memfs path to OS-appropriate display path.""" + if os_type == OSType.WINDOWS: + parts = memfs_path.split("/") + if parts and len(parts[0]) == 1 and parts[0].isalpha(): + drive = parts[0].upper() + rest = "\\".join(parts[1:]) + return f"{drive}:\\{rest}" if rest else f"{drive}:\\" + return "\\".join(parts) + return "/" + memfs_path if memfs_path else "/" + + +def _make_file_content(filename: str, seed: int) -> str: + """Deterministic file content for a given filename and seed.""" + return f"[Content of {filename} | seed={seed} | size=1024]" + + +def _pick_subdir(rng: random.Random, os_type: OSType) -> str: + """Pick a random subdirectory under home.""" + home = _home_dir(os_type) + subdir = rng.choice(DIRNAMES) + return f"{home}/{subdir}" + + +def _pick_remote_dir(rng: random.Random, remote_user: str) -> str: + """Pick a random directory on the remote machine.""" + options = [ + f"home/{remote_user}/{rng.choice(DIRNAMES)}", + f"var/www/html", + f"opt/{rng.choice(DIRNAMES)}", + f"home/{remote_user}", + ] + return rng.choice(options) + + +def _add_distractor_files( + fs_spec: list, base_dir: str, rng: random.Random, seed: int, count: int = 3 +): + """Add some irrelevant files to make the task more realistic.""" + used = {f["path"] for f in fs_spec} + for _ in range(count): + name = rng.choice(FILENAMES) + path = f"{base_dir}/{name}" + if path not in used: + content = _make_file_content(name, seed + hash(name) % 10000) + fs_spec.append({"path": path, "content": content}) + used.add(path) + + +# --------------------------------------------------------------------------- +# Single task generators +# --------------------------------------------------------------------------- + + +def _gen_upload(rng: random.Random, seed: int, os_type: OSType, + remote_host: str, remote_user: str) -> TerminalTask: + filename = rng.choice(FILENAMES) + local_dir = _pick_subdir(rng, os_type) + remote_dir = _pick_remote_dir(rng, remote_user) + content = _make_file_content(filename, seed) + + local_fs = [{"path": f"{local_dir}/{filename}", "content": content}] + _add_distractor_files(local_fs, local_dir, rng, seed) + remote_fs = [{"path": remote_dir, "is_dir": True}] + + local_display = _display_path(f"{local_dir}/{filename}", os_type) + remote_display = f"{remote_user}@{remote_host}:/{remote_dir}/{filename}" + + return TerminalTask( + seed=seed, local_os=os_type, task_category="single", + task_type=TaskType.UPLOAD.value, + remote_host=remote_host, remote_user=remote_user, + description=f"Upload '{local_display}' to {remote_display}", + goal_checks=[ + {"type": "file_exists", "machine": "remote", + "path": f"{remote_dir}/{filename}", "content": content}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"filename": filename, "local_dir": local_dir, "remote_dir": remote_dir}, + ) + + +def _gen_download(rng: random.Random, seed: int, os_type: OSType, + remote_host: str, remote_user: str) -> TerminalTask: + filename = rng.choice(FILENAMES) + local_dir = _pick_subdir(rng, os_type) + remote_dir = _pick_remote_dir(rng, remote_user) + content = _make_file_content(filename, seed) + + local_fs = [{"path": local_dir, "is_dir": True}] + _add_distractor_files(local_fs, local_dir, rng, seed) + remote_fs = [{"path": f"{remote_dir}/{filename}", "content": content}] + _add_distractor_files(remote_fs, remote_dir, rng, seed) + + local_display = _display_path(f"{local_dir}/{filename}", os_type) + remote_display = f"{remote_user}@{remote_host}:/{remote_dir}/{filename}" + + return TerminalTask( + seed=seed, local_os=os_type, task_category="single", + task_type=TaskType.DOWNLOAD.value, + remote_host=remote_host, remote_user=remote_user, + description=f"Download '{remote_display}' to '{local_display}'", + goal_checks=[ + {"type": "file_exists", "machine": "local", + "path": f"{local_dir}/{filename}", "content": content}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"filename": filename, "local_dir": local_dir, "remote_dir": remote_dir}, + ) + + +def _gen_rename(rng: random.Random, seed: int, os_type: OSType, + remote_host: str, remote_user: str) -> TerminalTask: + is_remote = rng.choice([True, False]) + old_name = rng.choice(FILENAMES) + # Generate a different new name + new_name = rng.choice(FILENAMES) + while new_name == old_name: + new_name = rng.choice(FILENAMES) + content = _make_file_content(old_name, seed) + + if is_remote: + target_dir = _pick_remote_dir(rng, remote_user) + fs_spec = [{"path": f"{target_dir}/{old_name}", "content": content}] + _add_distractor_files(fs_spec, target_dir, rng, seed) + machine_label = "remote" + desc_path = f"{remote_user}@{remote_host}:/{target_dir}" + else: + target_dir = _pick_subdir(rng, os_type) + fs_spec = [{"path": f"{target_dir}/{old_name}", "content": content}] + _add_distractor_files(fs_spec, target_dir, rng, seed) + machine_label = "local" + desc_path = _display_path(target_dir, os_type) + + local_fs = fs_spec if not is_remote else [] + remote_fs = fs_spec if is_remote else [] + + return TerminalTask( + seed=seed, local_os=os_type, task_category="single", + task_type=TaskType.RENAME.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Rename '{old_name}' to '{new_name}' in " + f"{'remote ' if is_remote else ''}{desc_path}"), + goal_checks=[ + {"type": "file_not_exists", "machine": machine_label, + "path": f"{target_dir}/{old_name}"}, + {"type": "file_exists", "machine": machine_label, + "path": f"{target_dir}/{new_name}", "content": content}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"is_remote": is_remote, "old_name": old_name, "new_name": new_name, + "target_dir": target_dir}, + ) + + +def _gen_move(rng: random.Random, seed: int, os_type: OSType, + remote_host: str, remote_user: str) -> TerminalTask: + is_remote = rng.choice([True, False]) + filename = rng.choice(FILENAMES) + content = _make_file_content(filename, seed) + + if is_remote: + src_dir = _pick_remote_dir(rng, remote_user) + dst_dir = _pick_remote_dir(rng, remote_user) + while dst_dir == src_dir: + dst_dir = _pick_remote_dir(rng, remote_user) + fs_spec = [ + {"path": f"{src_dir}/{filename}", "content": content}, + {"path": dst_dir, "is_dir": True}, + ] + _add_distractor_files(fs_spec, src_dir, rng, seed) + machine_label = "remote" + src_display = f"/{src_dir}/{filename}" + dst_display = f"/{dst_dir}/" + else: + src_dir = _pick_subdir(rng, os_type) + dst_dir = _pick_subdir(rng, os_type) + while dst_dir == src_dir: + dst_dir = _pick_subdir(rng, os_type) + fs_spec = [ + {"path": f"{src_dir}/{filename}", "content": content}, + {"path": dst_dir, "is_dir": True}, + ] + _add_distractor_files(fs_spec, src_dir, rng, seed) + machine_label = "local" + src_display = _display_path(f"{src_dir}/{filename}", os_type) + dst_display = _display_path(dst_dir, os_type) + + local_fs = fs_spec if not is_remote else [] + remote_fs = fs_spec if is_remote else [] + + return TerminalTask( + seed=seed, local_os=os_type, task_category="single", + task_type=TaskType.MOVE.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Move '{src_display}' to '{dst_display}'" + f"{' on remote server' if is_remote else ''}"), + goal_checks=[ + {"type": "file_not_exists", "machine": machine_label, + "path": f"{src_dir}/{filename}"}, + {"type": "file_exists", "machine": machine_label, + "path": f"{dst_dir}/{filename}", "content": content}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"is_remote": is_remote, "filename": filename, + "src_dir": src_dir, "dst_dir": dst_dir}, + ) + + +def _gen_chmod(rng: random.Random, seed: int, os_type: OSType, + remote_host: str, remote_user: str) -> TerminalTask: + # chmod only works on Linux/Mac or remote + is_remote = rng.choice([True, False]) + if not is_remote and os_type == OSType.WINDOWS: + is_remote = True # force remote for Windows local + + filename = rng.choice(FILENAMES) + content = _make_file_content(filename, seed) + old_perm = rng.choice(PERMISSION_MODES) + new_perm = rng.choice(PERMISSION_MODES) + while new_perm == old_perm: + new_perm = rng.choice(PERMISSION_MODES) + + if is_remote: + target_dir = _pick_remote_dir(rng, remote_user) + remote_fs = [{"path": f"{target_dir}/{filename}", "content": content, + "permissions": old_perm}] + _add_distractor_files(remote_fs, target_dir, rng, seed) + local_fs = [] + machine_label = "remote" + desc_path = f"{remote_user}@{remote_host}:/{target_dir}/{filename}" + else: + target_dir = _pick_subdir(rng, os_type) + local_fs = [{"path": f"{target_dir}/{filename}", "content": content, + "permissions": old_perm}] + _add_distractor_files(local_fs, target_dir, rng, seed) + remote_fs = [] + machine_label = "local" + desc_path = _display_path(f"{target_dir}/{filename}", os_type) + + return TerminalTask( + seed=seed, local_os=os_type, task_category="single", + task_type=TaskType.CHMOD.value, + remote_host=remote_host, remote_user=remote_user, + description=f"Change permissions of '{desc_path}' to {new_perm}", + goal_checks=[ + {"type": "permission_equals", "machine": machine_label, + "path": f"{target_dir}/{filename}", "value": new_perm}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"is_remote": is_remote, "filename": filename, + "target_dir": target_dir, "new_perm": new_perm}, + ) + + +def _gen_delete(rng: random.Random, seed: int, os_type: OSType, + remote_host: str, remote_user: str) -> TerminalTask: + is_remote = rng.choice([True, False]) + filename = rng.choice(FILENAMES) + content = _make_file_content(filename, seed) + + if is_remote: + target_dir = _pick_remote_dir(rng, remote_user) + remote_fs = [{"path": f"{target_dir}/{filename}", "content": content}] + _add_distractor_files(remote_fs, target_dir, rng, seed) + local_fs = [] + machine_label = "remote" + desc_path = f"{remote_user}@{remote_host}:/{target_dir}/{filename}" + else: + target_dir = _pick_subdir(rng, os_type) + local_fs = [{"path": f"{target_dir}/{filename}", "content": content}] + _add_distractor_files(local_fs, target_dir, rng, seed) + remote_fs = [] + machine_label = "local" + desc_path = _display_path(f"{target_dir}/{filename}", os_type) + + return TerminalTask( + seed=seed, local_os=os_type, task_category="single", + task_type=TaskType.DELETE.value, + remote_host=remote_host, remote_user=remote_user, + description=f"Delete the file '{desc_path}'", + goal_checks=[ + {"type": "file_not_exists", "machine": machine_label, + "path": f"{target_dir}/{filename}"}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"is_remote": is_remote, "filename": filename, "target_dir": target_dir}, + ) + + +def _gen_copy(rng: random.Random, seed: int, os_type: OSType, + remote_host: str, remote_user: str) -> TerminalTask: + is_remote = rng.choice([True, False]) + filename = rng.choice(FILENAMES) + content = _make_file_content(filename, seed) + + if is_remote: + src_dir = _pick_remote_dir(rng, remote_user) + dst_dir = _pick_remote_dir(rng, remote_user) + while dst_dir == src_dir: + dst_dir = _pick_remote_dir(rng, remote_user) + fs_spec = [ + {"path": f"{src_dir}/{filename}", "content": content}, + {"path": dst_dir, "is_dir": True}, + ] + machine_label = "remote" + src_display = f"/{src_dir}/{filename}" + dst_display = f"/{dst_dir}/{filename}" + else: + src_dir = _pick_subdir(rng, os_type) + dst_dir = _pick_subdir(rng, os_type) + while dst_dir == src_dir: + dst_dir = _pick_subdir(rng, os_type) + fs_spec = [ + {"path": f"{src_dir}/{filename}", "content": content}, + {"path": dst_dir, "is_dir": True}, + ] + machine_label = "local" + src_display = _display_path(f"{src_dir}/{filename}", os_type) + dst_display = _display_path(f"{dst_dir}/{filename}", os_type) + + local_fs = fs_spec if not is_remote else [] + remote_fs = fs_spec if is_remote else [] + + return TerminalTask( + seed=seed, local_os=os_type, task_category="single", + task_type=TaskType.COPY.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Copy '{src_display}' to '{dst_display}'" + f"{' on remote server' if is_remote else ''}"), + goal_checks=[ + {"type": "file_exists", "machine": machine_label, + "path": f"{src_dir}/{filename}", "content": content}, + {"type": "file_exists", "machine": machine_label, + "path": f"{dst_dir}/{filename}", "content": content}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"is_remote": is_remote, "filename": filename, + "src_dir": src_dir, "dst_dir": dst_dir}, + ) + + +def _gen_pack(rng: random.Random, seed: int, os_type: OSType, + remote_host: str, remote_user: str) -> TerminalTask: + is_remote = rng.choice([True, False]) + fmt = rng.choice(ARCHIVE_FORMATS) + # On Windows local, only zip is available (no tar) + if not is_remote and os_type == OSType.WINDOWS and fmt.startswith("tar"): + fmt = "zip" + + dir_name = rng.choice(DIRNAMES) + n_files = rng.randint(2, 4) + files = rng.sample(FILENAMES, min(n_files, len(FILENAMES))) + + if fmt == "zip": + archive_name = f"{dir_name}.zip" + elif fmt == "tar.gz": + archive_name = f"{dir_name}.tar.gz" + else: + archive_name = f"{dir_name}.tar" + + if is_remote: + base_dir = _pick_remote_dir(rng, remote_user) + machine_label = "remote" + else: + base_dir = _pick_subdir(rng, os_type) + machine_label = "local" + + src_dir = f"{base_dir}/{dir_name}" + archive_entries = {} + fs_spec = [] + for f in files: + content = _make_file_content(f, seed) + fs_spec.append({"path": f"{src_dir}/{f}", "content": content}) + archive_entries[f"{dir_name}/{f}"] = content + + local_fs = fs_spec if not is_remote else [] + remote_fs = fs_spec if is_remote else [] + + if is_remote: + desc = f"Pack the directory '/{src_dir}' into '{archive_name}' on the remote server" + else: + desc = f"Pack the directory '{_display_path(src_dir, os_type)}' into '{archive_name}'" + + return TerminalTask( + seed=seed, local_os=os_type, task_category="single", + task_type=TaskType.PACK.value, + remote_host=remote_host, remote_user=remote_user, + description=desc, + goal_checks=[ + {"type": "archive_contains", "machine": machine_label, + "path": f"{base_dir}/{archive_name}", + "entries": archive_entries, "archive_type": fmt}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"is_remote": is_remote, "dir_name": dir_name, "archive_name": archive_name, + "fmt": fmt, "base_dir": base_dir, "files": files}, + ) + + +def _gen_mkdir(rng: random.Random, seed: int, os_type: OSType, + remote_host: str, remote_user: str) -> TerminalTask: + is_remote = rng.choice([True, False]) + depth = rng.randint(2, 4) + parts = rng.sample(DIRNAMES, min(depth, len(DIRNAMES))) + + if is_remote: + base = f"home/{remote_user}" + machine_label = "remote" + else: + base = _home_dir(os_type) + machine_label = "local" + + target_path = base + "/" + "/".join(parts) + + if is_remote: + desc = f"Create the directory structure '/{target_path}' on the remote server" + else: + desc = f"Create the directory structure '{_display_path(target_path, os_type)}'" + + return TerminalTask( + seed=seed, local_os=os_type, task_category="single", + task_type=TaskType.MKDIR.value, + remote_host=remote_host, remote_user=remote_user, + description=desc, + goal_checks=[ + {"type": "dir_exists", "machine": machine_label, "path": target_path}, + ], + local_fs_spec=[], remote_fs_spec=[], + params={"is_remote": is_remote, "target_path": target_path}, + ) + + +SINGLE_GENERATORS = { + TaskType.UPLOAD: _gen_upload, + TaskType.DOWNLOAD: _gen_download, + TaskType.RENAME: _gen_rename, + TaskType.MOVE: _gen_move, + TaskType.CHMOD: _gen_chmod, + TaskType.DELETE: _gen_delete, + TaskType.COPY: _gen_copy, + TaskType.PACK: _gen_pack, + TaskType.MKDIR: _gen_mkdir, +} + + +# --------------------------------------------------------------------------- +# Composite task generators +# --------------------------------------------------------------------------- + + +def _gen_pack_upload(rng, seed, os_type, remote_host, remote_user): + """Pack local files into archive, then upload archive to remote.""" + fmt = rng.choice(ARCHIVE_FORMATS) + if os_type == OSType.WINDOWS and fmt.startswith("tar"): + fmt = "zip" + + dir_name = rng.choice(DIRNAMES) + files = rng.sample(FILENAMES, rng.randint(2, 4)) + local_dir = _pick_subdir(rng, os_type) + remote_dir = _pick_remote_dir(rng, remote_user) + + ext = {"tar": ".tar", "tar.gz": ".tar.gz", "zip": ".zip"}[fmt] + archive_name = f"{dir_name}{ext}" + src_dir = f"{local_dir}/{dir_name}" + + local_fs = [] + archive_entries = {} + for f in files: + content = _make_file_content(f, seed) + local_fs.append({"path": f"{src_dir}/{f}", "content": content}) + archive_entries[f"{dir_name}/{f}"] = content + + remote_fs = [{"path": remote_dir, "is_dir": True}] + + local_display = _display_path(src_dir, os_type) + return TerminalTask( + seed=seed, local_os=os_type, task_category="composite", + task_type=CompositeTemplate.PACK_UPLOAD.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Pack '{local_display}' into a {fmt} archive and upload it to " + f"{remote_user}@{remote_host}:/{remote_dir}/"), + goal_checks=[ + {"type": "archive_contains", "machine": "remote", + "path": f"{remote_dir}/{archive_name}", + "entries": archive_entries, "archive_type": fmt}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"fmt": fmt, "dir_name": dir_name, "archive_name": archive_name, + "local_dir": local_dir, "remote_dir": remote_dir, "files": files}, + ) + + +def _gen_download_extract(rng, seed, os_type, remote_host, remote_user): + """Download archive from remote, extract locally.""" + fmt = rng.choice(ARCHIVE_FORMATS) + if os_type == OSType.WINDOWS and fmt.startswith("tar"): + fmt = "zip" + + dir_name = rng.choice(DIRNAMES) + files = rng.sample(FILENAMES, rng.randint(2, 4)) + local_dir = _pick_subdir(rng, os_type) + remote_dir = _pick_remote_dir(rng, remote_user) + + ext = {"tar": ".tar", "tar.gz": ".tar.gz", "zip": ".zip"}[fmt] + archive_name = f"{dir_name}{ext}" + + archive_entries = {} + for f in files: + content = _make_file_content(f, seed) + archive_entries[f"{dir_name}/{f}"] = content + + archive_content = json.dumps({"type": fmt, "entries": archive_entries}) + remote_fs = [ + {"path": f"{remote_dir}/{archive_name}", "content": archive_content, + "archive_type": fmt, "archive_entries": archive_entries}, + ] + local_fs = [{"path": local_dir, "is_dir": True}] + + # Goal: all files extracted locally + checks = [] + for rel, content in archive_entries.items(): + checks.append({ + "type": "file_exists", "machine": "local", + "path": f"{local_dir}/{rel}", "content": content, + }) + + return TerminalTask( + seed=seed, local_os=os_type, task_category="composite", + task_type=CompositeTemplate.DOWNLOAD_EXTRACT.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Download '{archive_name}' from " + f"{remote_user}@{remote_host}:/{remote_dir}/ " + f"and extract its contents to " + f"'{_display_path(local_dir, os_type)}'"), + goal_checks=checks, + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"fmt": fmt, "dir_name": dir_name, "archive_name": archive_name, + "local_dir": local_dir, "remote_dir": remote_dir}, + ) + + +def _gen_mkdir_upload(rng, seed, os_type, remote_host, remote_user): + """Create directory on remote, then upload file there.""" + filename = rng.choice(FILENAMES) + content = _make_file_content(filename, seed) + local_dir = _pick_subdir(rng, os_type) + # Remote dir that does NOT exist yet + depth = rng.randint(2, 3) + parts = rng.sample(DIRNAMES, depth) + remote_dir = f"home/{remote_user}/" + "/".join(parts) + + local_fs = [{"path": f"{local_dir}/{filename}", "content": content}] + _add_distractor_files(local_fs, local_dir, rng, seed) + remote_fs = [] # remote dir intentionally does not exist + + return TerminalTask( + seed=seed, local_os=os_type, task_category="composite", + task_type=CompositeTemplate.MKDIR_UPLOAD.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Create the directory '/{remote_dir}' on the remote server, " + f"then upload '{_display_path(local_dir + '/' + filename, os_type)}' there"), + goal_checks=[ + {"type": "dir_exists", "machine": "remote", "path": remote_dir}, + {"type": "file_exists", "machine": "remote", + "path": f"{remote_dir}/{filename}", "content": content}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"filename": filename, "local_dir": local_dir, "remote_dir": remote_dir}, + ) + + +def _gen_upload_chmod(rng, seed, os_type, remote_host, remote_user): + """Upload file to remote, then change its permissions.""" + filename = rng.choice(FILENAMES) + content = _make_file_content(filename, seed) + local_dir = _pick_subdir(rng, os_type) + remote_dir = _pick_remote_dir(rng, remote_user) + target_perm = rng.choice(["755", "700", "600", "444"]) + + local_fs = [{"path": f"{local_dir}/{filename}", "content": content}] + remote_fs = [{"path": remote_dir, "is_dir": True}] + + return TerminalTask( + seed=seed, local_os=os_type, task_category="composite", + task_type=CompositeTemplate.UPLOAD_CHMOD.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Upload '{_display_path(local_dir + '/' + filename, os_type)}' to " + f"{remote_user}@{remote_host}:/{remote_dir}/ " + f"and set its permissions to {target_perm}"), + goal_checks=[ + {"type": "file_exists", "machine": "remote", + "path": f"{remote_dir}/{filename}", "content": content}, + {"type": "permission_equals", "machine": "remote", + "path": f"{remote_dir}/{filename}", "value": target_perm}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"filename": filename, "local_dir": local_dir, + "remote_dir": remote_dir, "target_perm": target_perm}, + ) + + +def _gen_upload_delete_source(rng, seed, os_type, remote_host, remote_user): + """Upload file to remote, then delete the local copy.""" + filename = rng.choice(FILENAMES) + content = _make_file_content(filename, seed) + local_dir = _pick_subdir(rng, os_type) + remote_dir = _pick_remote_dir(rng, remote_user) + + local_fs = [{"path": f"{local_dir}/{filename}", "content": content}] + _add_distractor_files(local_fs, local_dir, rng, seed) + remote_fs = [{"path": remote_dir, "is_dir": True}] + + return TerminalTask( + seed=seed, local_os=os_type, task_category="composite", + task_type=CompositeTemplate.UPLOAD_DELETE_SOURCE.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Upload '{_display_path(local_dir + '/' + filename, os_type)}' to " + f"{remote_user}@{remote_host}:/{remote_dir}/ " + f"and then delete the local copy"), + goal_checks=[ + {"type": "file_exists", "machine": "remote", + "path": f"{remote_dir}/{filename}", "content": content}, + {"type": "file_not_exists", "machine": "local", + "path": f"{local_dir}/{filename}"}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"filename": filename, "local_dir": local_dir, "remote_dir": remote_dir}, + ) + + +def _gen_pack_upload_extract(rng, seed, os_type, remote_host, remote_user): + """Pack local dir, upload, extract on remote.""" + fmt = rng.choice(ARCHIVE_FORMATS) + if os_type == OSType.WINDOWS and fmt.startswith("tar"): + fmt = "zip" + + dir_name = rng.choice(DIRNAMES) + files = rng.sample(FILENAMES, rng.randint(2, 4)) + local_dir = _pick_subdir(rng, os_type) + remote_dir = _pick_remote_dir(rng, remote_user) + + src_dir = f"{local_dir}/{dir_name}" + local_fs = [] + expected_files = {} + for f in files: + content = _make_file_content(f, seed) + local_fs.append({"path": f"{src_dir}/{f}", "content": content}) + expected_files[f"{dir_name}/{f}"] = content + + remote_fs = [{"path": remote_dir, "is_dir": True}] + + # Goal: files extracted on remote + checks = [] + for rel, content in expected_files.items(): + checks.append({ + "type": "file_exists", "machine": "remote", + "path": f"{remote_dir}/{rel}", "content": content, + }) + + return TerminalTask( + seed=seed, local_os=os_type, task_category="composite", + task_type=CompositeTemplate.PACK_UPLOAD_EXTRACT.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Pack '{_display_path(src_dir, os_type)}' into a {fmt} archive, " + f"upload to {remote_user}@{remote_host}:/{remote_dir}/, " + f"and extract it there"), + goal_checks=checks, + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"fmt": fmt, "dir_name": dir_name, "local_dir": local_dir, + "remote_dir": remote_dir, "files": files}, + ) + + +def _gen_download_rename(rng, seed, os_type, remote_host, remote_user): + """Download file from remote, rename locally.""" + old_name = rng.choice(FILENAMES) + new_name = rng.choice(FILENAMES) + while new_name == old_name: + new_name = rng.choice(FILENAMES) + content = _make_file_content(old_name, seed) + local_dir = _pick_subdir(rng, os_type) + remote_dir = _pick_remote_dir(rng, remote_user) + + remote_fs = [{"path": f"{remote_dir}/{old_name}", "content": content}] + local_fs = [{"path": local_dir, "is_dir": True}] + + return TerminalTask( + seed=seed, local_os=os_type, task_category="composite", + task_type=CompositeTemplate.DOWNLOAD_RENAME.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"Download '{old_name}' from " + f"{remote_user}@{remote_host}:/{remote_dir}/ " + f"and rename it to '{new_name}' in " + f"'{_display_path(local_dir, os_type)}'"), + goal_checks=[ + {"type": "file_exists", "machine": "local", + "path": f"{local_dir}/{new_name}", "content": content}, + {"type": "file_not_exists", "machine": "local", + "path": f"{local_dir}/{old_name}"}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"old_name": old_name, "new_name": new_name, + "local_dir": local_dir, "remote_dir": remote_dir}, + ) + + +def _gen_backup_replace(rng, seed, os_type, remote_host, remote_user): + """Backup existing remote file to .bak, upload new version.""" + filename = rng.choice(FILENAMES) + old_content = _make_file_content(filename, seed) + new_content = _make_file_content(filename, seed + 99999) + local_dir = _pick_subdir(rng, os_type) + remote_dir = _pick_remote_dir(rng, remote_user) + + local_fs = [{"path": f"{local_dir}/{filename}", "content": new_content}] + remote_fs = [{"path": f"{remote_dir}/{filename}", "content": old_content}] + + bak_name = filename + ".bak" + + return TerminalTask( + seed=seed, local_os=os_type, task_category="composite", + task_type=CompositeTemplate.BACKUP_REPLACE.value, + remote_host=remote_host, remote_user=remote_user, + description=(f"On the remote server, rename '/{remote_dir}/{filename}' to " + f"'{bak_name}' as a backup, then upload the new version from " + f"'{_display_path(local_dir + '/' + filename, os_type)}'"), + goal_checks=[ + {"type": "file_exists", "machine": "remote", + "path": f"{remote_dir}/{bak_name}", "content": old_content}, + {"type": "file_exists", "machine": "remote", + "path": f"{remote_dir}/{filename}", "content": new_content}, + ], + local_fs_spec=local_fs, remote_fs_spec=remote_fs, + params={"filename": filename, "bak_name": bak_name, + "local_dir": local_dir, "remote_dir": remote_dir}, + ) + + +COMPOSITE_GENERATORS = { + CompositeTemplate.PACK_UPLOAD: _gen_pack_upload, + CompositeTemplate.DOWNLOAD_EXTRACT: _gen_download_extract, + CompositeTemplate.MKDIR_UPLOAD: _gen_mkdir_upload, + CompositeTemplate.UPLOAD_CHMOD: _gen_upload_chmod, + CompositeTemplate.UPLOAD_DELETE_SOURCE: _gen_upload_delete_source, + CompositeTemplate.PACK_UPLOAD_EXTRACT: _gen_pack_upload_extract, + CompositeTemplate.DOWNLOAD_RENAME: _gen_download_rename, + CompositeTemplate.BACKUP_REPLACE: _gen_backup_replace, +} + + +# --------------------------------------------------------------------------- +# Main entry point +# --------------------------------------------------------------------------- + + +def generate_task( + seed: int, + composite_ratio: float = 0.5, + forced_task_type: Optional[str] = None, +) -> TerminalTask: + """Generate a complete terminal task from a seed. + + Args: + seed: Random seed that fully determines the task. + composite_ratio: Probability of generating a composite task (0-1). + forced_task_type: If set, force this task type (TaskType or CompositeTemplate value). + """ + rng = random.Random(seed) + + # Pick OS + local_os = rng.choice(list(OSType)) + + # Pick remote server + remote_host = f"192.168.1.{rng.randint(10, 99)}" + remote_user = rng.choice(REMOTE_USERS) + + # Decide task type + if forced_task_type: + # Try single first, then composite + try: + tt = TaskType(forced_task_type) + return SINGLE_GENERATORS[tt](rng, seed, local_os, remote_host, remote_user) + except ValueError: + ct = CompositeTemplate(forced_task_type) + return COMPOSITE_GENERATORS[ct](rng, seed, local_os, remote_host, remote_user) + + is_composite = rng.random() < composite_ratio + if is_composite: + template = rng.choice(list(CompositeTemplate)) + return COMPOSITE_GENERATORS[template](rng, seed, local_os, remote_host, remote_user) + else: + task_type = rng.choice(list(TaskType)) + return SINGLE_GENERATORS[task_type](rng, seed, local_os, remote_host, remote_user) + + +# --------------------------------------------------------------------------- +# Build environment from task +# --------------------------------------------------------------------------- + + +def build_env_from_task(task: TerminalTask, max_steps: int = 15): + """Construct a TerminalEnv from a TerminalTask specification.""" + from trinity.common.workflows.connect_the_dots.terminal.commands import ( + build_command_registry, + ) + + os_type = task.local_os + + # Build local machine + home = _home_dir(os_type) + local_fs = VirtualFS() + local_fs.makedirs(home) + if os_type == OSType.WINDOWS: + env_vars = { + "USERPROFILE": _display_path(home, os_type), + "USERNAME": "user", + "CD": _display_path(home, os_type), + } + else: + env_vars = { + "HOME": _display_path(home, os_type), + "USER": "user", + "PWD": _display_path(home, os_type), + } + + for spec in task.local_fs_spec: + path = spec["path"] + if spec.get("is_dir"): + local_fs.makedirs(path) + else: + parent = "/".join(path.split("/")[:-1]) + if parent: + local_fs.makedirs(parent) + local_fs.writetext(path, spec.get("content", "")) + meta = local_fs.get_meta(path) + if "permissions" in spec: + meta.permissions = spec["permissions"] + if "archive_type" in spec: + meta.archive_type = spec["archive_type"] + meta.archive_entries = spec.get("archive_entries") + + local = MachineState( + os_type=os_type, hostname="localhost", username="user", + home_dir=home, cwd=home, fs=local_fs, env_vars=env_vars, + ) + + # Build remote machine + remote_home = f"home/{task.remote_user}" + remote_fs = VirtualFS() + remote_fs.makedirs(remote_home) + + for spec in task.remote_fs_spec: + path = spec["path"] + if spec.get("is_dir"): + remote_fs.makedirs(path) + else: + parent = "/".join(path.split("/")[:-1]) + if parent: + remote_fs.makedirs(parent) + remote_fs.writetext(path, spec.get("content", "")) + meta = remote_fs.get_meta(path) + if "permissions" in spec: + meta.permissions = spec["permissions"] + if "archive_type" in spec: + meta.archive_type = spec["archive_type"] + meta.archive_entries = spec.get("archive_entries") + + remote = MachineState( + os_type=OSType.LINUX, hostname=task.remote_host, username=task.remote_user, + home_dir=remote_home, cwd=remote_home, fs=remote_fs, + env_vars={ + "HOME": f"/home/{task.remote_user}", + "USER": task.remote_user, + "IP": task.remote_host, + }, + ) + + handlers = build_command_registry() + return TerminalEnv(local, remote, handlers, max_steps=max_steps) diff --git a/trinity/common/workflows/connect_the_dots/terminal/workflow.py b/trinity/common/workflows/connect_the_dots/terminal/workflow.py new file mode 100644 index 00000000000..dc1e9f0cbaa --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/terminal/workflow.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 -*- +""" +CoD Terminal workflow for file operation tasks. + +Extends AsyncCoDMultiStepWorkflow following the same patterns as +CoDFrozenLakeWorkflow and CoDRandomAlchemyWorkflow. +""" + +from typing import List, Optional, Tuple + +from trinity.common.experience import Experience +from trinity.common.models.model import ModelWrapper +from trinity.common.workflows.connect_the_dots.base_workflow import ( + AsyncCoDMultiStepWorkflow, +) +from trinity.common.workflows.connect_the_dots.utils import extract_content_between_keys +from trinity.common.workflows.connect_the_dots.terminal.env import ( + OSType, + TerminalEnv, +) +from trinity.common.workflows.connect_the_dots.terminal.goal_check import ( + check_goal, +) +from trinity.common.workflows.connect_the_dots.terminal.prompts import ( + load_system_prompt, + load_user_prompt, +) +from trinity.common.workflows.connect_the_dots.terminal.task_gen import ( + TerminalTask, + build_env_from_task, + generate_task, +) +from trinity.common.workflows.workflow import Task + + +_OS_DISPLAY = { + OSType.WINDOWS: "Windows", + OSType.MAC: "macOS", + OSType.LINUX: "Linux", +} + + +def _parse_action(response: str) -> Optional[str]: + """Extract command from ... tags.""" + content, success = extract_content_between_keys(response, "", "") + if not success: + return None + cmd = content.strip() + return cmd if cmd else None + + +class CoDTerminalWorkflow(AsyncCoDMultiStepWorkflow): + """CoD workflow for simulated terminal file-operation tasks.""" + + is_async: bool = True + + def __init__( + self, + model: ModelWrapper, + task: Task, + auxiliary_models: Optional[List] = None, + use_openai_client: bool = False, + ): + super().__init__( + task=task, + model=model, + auxiliary_models=auxiliary_models, + use_openai_client=use_openai_client, + ) + + workflow_args = task.workflow_args if hasattr(task, "workflow_args") else {} + self.agent_max_steps: int = workflow_args.get("agent_max_steps", 15) + self.max_response_tokens_restraint = workflow_args.get( + "max_response_tokens_restraint", None + ) + composite_ratio: float = workflow_args.get("composite_ratio", 0.5) + + # Reconstruct task from seed + raw_task = task.raw_task if hasattr(task, "raw_task") else {} + self.seed: int = raw_task.get("seed", 42) + forced_task_type = raw_task.get("forced_task_type", None) + + self.terminal_task: TerminalTask = generate_task( + seed=self.seed, + composite_ratio=composite_ratio, + forced_task_type=forced_task_type, + ) + self.env: Optional[TerminalEnv] = None + + # State + self.last_output: str = "" + self.format_error: bool = False + self.early_completion: bool = False + + async def run_async(self) -> List[Experience]: + self.env = build_env_from_task(self.terminal_task, max_steps=self.agent_max_steps) + self.last_output = "" + self.format_error = False + self.early_completion = False + + os_type = self.terminal_task.local_os + sys_prompt = load_system_prompt( + os_type=os_type.value, + os_name=_OS_DISPLAY[os_type], + remote_user=self.terminal_task.remote_user, + remote_host=self.terminal_task.remote_host, + task_description=self.terminal_task.description, + max_steps=self.agent_max_steps, + ) + sys_prompt = self._augment_system_prompt(sys_prompt) + + self.memory.clear() + self.memory.append({"role": "system", "content": sys_prompt}) + + return await super().run_async() + + async def step_async(self, step_num: int) -> Tuple[bool, List[Experience]]: + # Build user prompt + terminal_prompt = self.env.get_prompt_string() + user_content = load_user_prompt( + current_step=step_num + 1, + max_steps=self.agent_max_steps, + command_output=self.last_output if step_num > 0 else None, + terminal_prompt=terminal_prompt, + ) + + if self.icl_examples and step_num == 0: + user_content = ( + f"{user_content}\n\n" + f"Here are some reference examples:\n\n{self.icl_examples}" + ) + + self.memory.append({"role": "user", "content": user_content}) + + if self.reply_prefix: + self.memory.append({"role": "assistant", "content": self.reply_prefix}) + + # Get model response + experiences = await self.model.chat_async(self.memory) + response_text = experiences[0].response_text + self.memory.append({"role": "assistant", "content": response_text}) + + # Store prompt info on experiences + sys_prompt = ( + self.memory[0]["content"] + if self.memory and self.memory[0]["role"] == "system" + else "" + ) + for exp in experiences: + exp.info["sys_prompt"] = sys_prompt + exp.info["user_prompt"] = user_content + + # Parse action + action = _parse_action(response_text) + if action is None: + self.format_error = True + self.last_output = ( + "ERROR: Could not parse command from ... tags. " + "Episode terminated." + ) + return False, experiences + + # Execute command via gym-style step + def _goal_fn(env): + return check_goal(self.terminal_task, env) + + observation, reward, done, info = self.env.step( + action, goal_check_fn=_goal_fn + ) + self.last_output = observation + + if info.get("early_completion"): + self.early_completion = True + self.last_output = "Task completed successfully." + + return not done, experiences + + async def reward_async(self, exps: List[Experience]) -> float: + if self.format_error: + reward = 0.0 + elif self.early_completion: + reward = 1.0 + else: + reward = check_goal(self.terminal_task, self.env) + + if exps: + # Feedback + if self.format_error: + feedback = "Failed: Episode terminated due to action format error." + elif reward > 0.5: + feedback = "Success! Task completed correctly." + else: + feedback = "Failed: Goal state not achieved." + + exps[-1].info["feedback"] = feedback + if exps[-1].metrics is None: + exps[-1].metrics = {} + exps[-1].metrics["format_error_termination"] = ( + 1.0 if self.format_error else 0.0 + ) + exps[-1].metrics["early_completion"] = ( + 1.0 if self.early_completion else 0.0 + ) + + return reward + + @property + def max_step_num(self) -> int: + return self.agent_max_steps diff --git a/trinity/common/workflows/connect_the_dots/update_context_workflow.py b/trinity/common/workflows/connect_the_dots/update_context_workflow.py new file mode 100644 index 00000000000..5ff76f77507 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/update_context_workflow.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +"""Update-context workflows for CoD packs. + +After a task in a pack is solved, the update-context episode reads the previous +context together with the solved trajectory and produces an updated context that +conditions the later tasks in the pack. + +``AsyncCoDUpdateContextWorkflow`` is the default implementation: it builds the +prompt, parses the response and scores it with a length-shaped reward, generating +through ``chat_async``. ``AsyncCoDUpdateContextAgentWorkflow`` inherits all of +that and replaces only the generation path with a single-shot AgentScope agent. +Override ``build_messages`` / ``parse_context`` / ``compute_reward`` for a +different context-update strategy. +""" + +from dataclasses import asdict +from typing import List, Optional, Tuple + +from trinity.common.experience import Experience +from trinity.common.models.model import ModelWrapper +from trinity.common.workflows.connect_the_dots.agentscope_utils import ( + build_agentscope_single_turn_agent, +) +from trinity.common.workflows.connect_the_dots.cod_utils import CoDPrompts +from trinity.common.workflows.workflow import Task, Workflow + + +class AsyncCoDUpdateContextWorkflow(Workflow): + """Async workflow for the CoD update-context episode. + + ``CoDWorkflow`` runs it once per task transition within a pack. The previous + context, solved trajectory, reward and feedback are injected via + ``set_context_inputs`` before each ``run_async``. The default strategy + iteratively refines a ``Hints:`` block and scores it with a length-shaped + reward; generation goes through ``chat_async``. + """ + + can_reset: bool = True + can_repeat: bool = False + is_async: bool = True + + def __init__( + self, + *, + task: Task, + model: ModelWrapper, + auxiliary_models: Optional[List[ModelWrapper]] = None, + ): + super().__init__(task=task, model=model, auxiliary_models=auxiliary_models) + # Inputs for the next context update, set via set_context_inputs. + self.prev_context: str = "" + self.trajectory: str = "" + self.reward: float = 0.0 + self.feedback: str = "" + self.reset(task) + + def reset(self, task: Task): + """Bind task-derived config and clear the per-call inputs.""" + self.task = task + self.prev_context = "" + self.trajectory = "" + self.reward = 0.0 + self.feedback = "" + + @property + def rollout_args(self): + return asdict(self.task.rollout_args) + + def set_context_inputs( + self, *, prev_context: str, trajectory: str, reward: float, feedback: str + ) -> None: + """Set the inputs for the next context update.""" + self.prev_context = prev_context + self.trajectory = trajectory + self.reward = reward + self.feedback = feedback + + def _append_token_limit(self, prompt: str) -> str: + """Append a response-length instruction when configured.""" + max_tokens = self.task.workflow_args.get("max_response_tokens_restraint") + if max_tokens: + prompt += f"\n\nPlease limit your response to {max_tokens} tokens." + return prompt + + def build_messages(self) -> List[dict]: + """Build the chat messages that ask the model to refine the hints.""" + hint_example = self.task.workflow_args.get("hint_example", False) + sys_prompt = CoDPrompts.sys_prompt_gen_hint_iteratively(hint_example) + sys_prompt = self._append_token_limit(sys_prompt) + user_prompt = CoDPrompts.user_prompt_gen_hint_iteratively( + prev_hint=self.prev_context, + trajectory=self.trajectory, + reward=self.reward, + feedback=self.feedback, + ) + return [ + {"role": "system", "content": sys_prompt}, + {"role": "user", "content": user_prompt}, + ] + + def parse_context(self, response: str) -> Tuple[str, bool]: + """Extract the updated context and a parse-success flag from the response.""" + return CoDPrompts.extract_hint(response) + + def compute_reward(self, exp: Experience, parse_success: bool) -> float: + """Length-shaped penalty for the generated context. + + Penalizes a parse failure, an over-short response, or an over-long one + ramped from ``len_zero_penalty`` to ``len_max_penalty``, scaled by + ``hint_penalty_coef``; returns 0 when these penalties are unset. Override + for a different reward. + """ + args = self.task.workflow_args + hint_penalty_coef = args.get("hint_penalty_coef", 0.0) + len_zero_penalty = args.get("len_zero_penalty", None) + len_max_penalty = args.get("len_max_penalty", None) + len_min_penalty = args.get("len_min_penalty", None) + + # Parse failure is the harshest signal: full penalty regardless of length. + if not parse_success: + return -1.0 * hint_penalty_coef + if (len_zero_penalty is None) or (len_max_penalty is None): + return 0.0 + assert ( + len_zero_penalty < len_max_penalty + ), "len_zero_penalty must be smaller than len_max_penalty." + + resp_len = len(exp.tokens) - exp.prompt_length + if len_min_penalty is not None and resp_len < len_min_penalty: + return -1.0 * hint_penalty_coef + ramp = min( + 1.0, + max( + 0.0, + (resp_len - len_zero_penalty) / (len_max_penalty - len_zero_penalty), + ), + ) + return -1.0 * hint_penalty_coef * ramp + + def _finalize(self, exp: Experience, messages: List[dict]) -> List[Experience]: + """Parse the response, score it, and attach the context to ``exp.info``.""" + new_context, parse_success = self.parse_context(exp.response_text or "") + exp.reward = self.compute_reward(exp, parse_success) + + if exp.metrics is None: + exp.metrics = {} + exp.metrics["hint_parse_success"] = 1.0 * parse_success + + exp.info["sys_prompt"] = messages[0]["content"] + exp.info["user_prompt"] = messages[-1]["content"] + exp.info["hint_parse_success"] = parse_success + exp.info["hint"] = new_context + exp.info["prev_hint"] = self.prev_context + return [exp] + + async def run_async(self) -> List[Experience]: + """Generate one updated context and return it as a single experience. + + The updated context is returned via ``exp.info["hint"]`` so callers do + not depend on the parsing details. + """ + messages = self.build_messages() + rollout_args = self.rollout_args + rollout_args["n"] = 1 + exps = await self.model.chat_async(messages, **rollout_args) + return self._finalize(exps[0], messages) + + +class AsyncCoDUpdateContextAgentWorkflow(AsyncCoDUpdateContextWorkflow): + """Update-context episode whose generation runs through an AgentScope agent. + + Prompt building, parsing and reward are inherited from the base workflow; + only the generation path is replaced with a single-shot AgentScope agent on + an isolated-history model clone. + """ + + requires_isolated_model_history: bool = True + + def __init__( + self, + *, + task: Task, + model: ModelWrapper, + auxiliary_models: Optional[List[ModelWrapper]] = None, + ): + super().__init__(task=task, model=model, auxiliary_models=auxiliary_models) + self._agent = None + self._agent_rollout_args = None + + async def _ensure_agent(self): + rollout_args = asdict(self.task.rollout_args) + if self._agent is None or self._agent_rollout_args != rollout_args: + self._agent = await build_agentscope_single_turn_agent( + name="cod_context_update", + model=self.model, + rollout_args=self.task.rollout_args, + ) + self._agent_rollout_args = rollout_args + return self._agent + + async def run_async(self) -> List[Experience]: + """Generate one updated context through the AgentScope agent.""" + self.model.history.clear() + messages = self.build_messages() + agent = await self._ensure_agent() + await agent(messages) + exp = self.model.extract_experience_from_history()[-1] + return self._finalize(exp, messages) diff --git a/trinity/common/workflows/connect_the_dots/utils.py b/trinity/common/workflows/connect_the_dots/utils.py new file mode 100644 index 00000000000..beb726a26e1 --- /dev/null +++ b/trinity/common/workflows/connect_the_dots/utils.py @@ -0,0 +1,85 @@ +"""General utils""" + +import hashlib +import json +import xml.etree.ElementTree as ET +from typing import Iterable, Optional, Tuple + + +def compute_stable_pack_seed(identities: Iterable[Tuple[int, int]]) -> int: + """Compute a deterministic seed from a pack's dataset identities.""" + payload = json.dumps(sorted(identities), separators=(",", ":")) + return int.from_bytes(hashlib.sha256(payload.encode()).digest()[:8], "big") + + +def extract_content_between_keys( + response: str, + key_start: str, + key_end: str, +) -> Tuple[str, bool]: + """Extract content in response between key_start and key_end. + + Strict success condition: + there is one and only one match for key_start / key_end, + and they should be in the correct order. + + Returns: + content (str): extracted content, "null" if failed. + success (bool): whether extraction is successful. + + TODO: consider requiring that key_end must appear at the end of response. + """ + + idx_start, idx_start_r = response.find(key_start), response.rfind(key_start) + idx_end, idx_end_r = response.find(key_end), response.rfind(key_end) + + if (idx_start == -1) or (idx_start != idx_start_r) or (idx_end == -1) or (idx_end != idx_end_r): + return "null", False + + if idx_start > idx_end: + return "null", False + + return response[(idx_start + len(key_start)) : idx_end], True + + +def parse_xml_answer( + response: str, + list_tags: Optional[set[str]] = None, +) -> Tuple[Optional[dict], str]: + """Parse one XML action wrapped in a unique ```` block.""" + content, success = extract_content_between_keys(response, "", "") + if not success: + return None, "expected_exactly_one_answer_tag" + + def element_value(element: ET.Element): + children = list(element) + if not children: + if element.attrib: + return dict(element.attrib) + return (element.text or "").strip() + if (element.text or "").strip() or any( + (child.tail or "").strip() for child in children + ): + raise ValueError("unexpected XML text") + if element.tag in (list_tags or set()): + return [element_value(child) for child in children] + value = dict(element.attrib) + for child in children: + child_value = element_value(child) + if child.tag in value: + current = value[child.tag] + value[child.tag] = ( + current + [child_value] + if isinstance(current, list) + else [current, child_value] + ) + else: + value[child.tag] = child_value + return value + + try: + action = ET.fromstring(content) + args = element_value(action) + return {"action": action.tag, "args": {} if args == "" else args}, "" + except (ET.ParseError, ValueError): + return None, "invalid_answer_xml" diff --git a/trinity/common/workflows/workflow.py b/trinity/common/workflows/workflow.py index 25853322fd9..c06cfe9804a 100644 --- a/trinity/common/workflows/workflow.py +++ b/trinity/common/workflows/workflow.py @@ -110,6 +110,16 @@ def __init__( self.run_id_base = 0 self.logger = get_logger(__name__) + # !!! Additional fields for CoD project !!! + # - hint: injected into system prompt to guide model's reasoning + # - max_response_tokens_restraint: injected into system prompt to guide model's response length + # - icl_examples: in-context learning examples, added only in first turn + # - reply_prefix: prefix for model's reply to guide model's generation + self.hint: Optional[str] = None + self.max_response_tokens_restraint: Optional[int] = None + self.icl_examples: Optional[str] = None + self.reply_prefix: Optional[str] = None # not really used in CoD + @property def resettable(self): """Deprecated, use cls.can_reset instead.""" @@ -132,6 +142,18 @@ def reset(self, task: Task): """Reset the workflow.""" raise NotImplementedError + def set_hint(self, hint: str): + """!!! For CoD: set the hint !!!""" + self.hint = hint + + def set_max_response_tokens_restraint(self, value: int): + """!!! For CoD: set the max response tokens restraint !!!""" + self.max_response_tokens_restraint = value + + def set_icl_examples(self, icl_examples: str): + """!!! For CoD: set ICL examples to be prepended to user message !!!""" + self.icl_examples = icl_examples + def set_repeat_times(self, repeat_times: int, run_id_base: int) -> None: """ Set the number of times to repeat the workflow. @@ -270,6 +292,11 @@ def reset(self, task: Task): else: raise ValueError("`reward_fn` must be a subclass of `RewardFn`") + # !!! Reset fields for CoD !!! + self.hint = None + self.max_response_tokens_restraint = None + self.icl_examples = None + def set_repeat_times(self, repeat_times, run_id_base): self.repeat_times = repeat_times self.task.rollout_args.n = repeat_times @@ -290,6 +317,21 @@ def format_messages(self): return messages +# util for cod +def log_sys_user_prompts_in_exp(messages, responses) -> None: + sys_prompt = "(not found in messages)" + user_prompt = "(not found in messages)" + for j, msg in enumerate(messages): + if j == 0 and msg["role"] == "system": + sys_prompt = msg["content"] + if msg["role"] == "user": + user_prompt = msg["content"] + break + for response in responses: + response.info["sys_prompt"] = sys_prompt + response.info["user_prompt"] = user_prompt + + class SimpleWorkflow(BaseSimpleWorkflow): """A workflow for simple single-round task.""" @@ -318,6 +360,11 @@ def run(self) -> List[Experience]: self.logger.debug( f"self.task_desc: {self.task_desc}, messages: {messages}, response: {response.response_text}, reward: {reward}" ) + + # !!! PATCH FOR COD START !!! + log_sys_user_prompts_in_exp(messages, responses) + # !!! PATCH FOR COD END !!! + return responses @@ -346,6 +393,11 @@ async def run_async(self) -> List[Experience]: self.logger.debug( f"self.task_desc: {self.task_desc}, messages: {messages}, response: {response.response_text}, reward: {reward}" ) + + # !!! PATCH FOR COD START !!! + log_sys_user_prompts_in_exp(messages, responses) + # !!! PATCH FOR COD END !!! + return responses diff --git a/trinity/explorer/explorer.py b/trinity/explorer/explorer.py index 0188b14ee93..70b1f9ec04c 100644 --- a/trinity/explorer/explorer.py +++ b/trinity/explorer/explorer.py @@ -317,6 +317,20 @@ async def explore_step(self) -> bool: self.explore_start_time = time.time() try: tasks = await self.taskset.read() + # !!! PATCH FOR COD START !!! + from trinity.common.workflows.connect_the_dots.cod_workflow import ( + pack_tasks, + ) + + pack_size = self.config.cod.task_pack_size + cod_workflow_args = self.config.cod.cod_workflow_args + tasks = pack_tasks( + tasks, + pack_size, + cod_workflow_args, + pack_strategy=self.config.cod.packing_strategy, + ) + # !!! PATCH FOR COD END !!! except StopAsyncIteration: self.logger.warning("No more tasks to explore. Stop exploring.") await self.finish_current_steps() @@ -418,7 +432,8 @@ async def eval(self): f"Use '{self.config.buffer.explorer_input.default_eval_workflow_type}' for evaluation." ) - for eval_taskset_config in self.config.buffer.explorer_input.eval_tasksets: + eval_taskset_configs = self.config.buffer.explorer_input.eval_tasksets + for eval_taskset_id, eval_taskset_config in enumerate(eval_taskset_configs): self.logger.info( f"Evaluation on {eval_taskset_config.name} at step {self.explore_step_num} started." ) @@ -428,7 +443,27 @@ async def eval(self): eval_tasks = [] while True: try: - eval_tasks.extend(await eval_taskset.read()) + data = await eval_taskset.read() + # !!! PATCH FOR COD START !!! + from trinity.common.workflows.connect_the_dots.cod_workflow import ( + pack_tasks, + ) + + if self.config.cod.eval_task_pack_size: + pack_size = self.config.cod.eval_task_pack_size + else: + pack_size = self.config.cod.task_pack_size + cod_workflow_args = self.config.cod.cod_workflow_args + for task in data: + task.index["taskset_id"] = eval_taskset_id + data = pack_tasks( + data, + pack_size, + cod_workflow_args, + pack_strategy=self.config.cod.packing_strategy, + ) + # !!! PATCH FOR COD END !!! + eval_tasks.extend(data) except StopAsyncIteration: break assert ( diff --git a/trinity/explorer/workflow_runner.py b/trinity/explorer/workflow_runner.py index dcdcd2fb5ea..20135be0d23 100644 --- a/trinity/explorer/workflow_runner.py +++ b/trinity/explorer/workflow_runner.py @@ -486,8 +486,28 @@ def __init__( async def debug(self) -> None: """Run the debug workflow.""" - tasks = await self.taskset.read(batch_size=1) + await self.prepare() + + # !!! ORIGINAL !!! + # tasks = await self.taskset.read_async(batch_size=1) + # task = tasks[0] + # !!! PATCH FOR COD START !!! + pack_size = self.config.cod.task_pack_size + tasks = await self.taskset.read(batch_size=pack_size) + print(f"!!! original number of read tasks: {len(tasks)} !!!") + from trinity.common.workflows.connect_the_dots.cod_workflow import pack_tasks + + cod_workflow_args = self.config.cod.cod_workflow_args + tasks = pack_tasks( + tasks, + pack_size, + cod_workflow_args, + pack_strategy=self.config.cod.packing_strategy, + ) task = tasks[0] + task.batch_id = 1 + # !!! PATCH FOR COD END !!! + self.logger.info(f"Start debugging task:\n{task.raw_task}") if not self.enable_profiling: status, exp_payload = await self.run_task( @@ -506,7 +526,7 @@ async def debug(self) -> None: self.logger.info( f"Debugging failed, extracting {len(experiences)} experiences from history." ) - await self.sqlite_writer.write(experiences) + # await self.sqlite_writer.write_async(experiences) # !!! disable for CoD !!! if status.ok: print(f"Task {task.task_id} completed successfully with metrics:\n{status.metrics}") else: diff --git a/trinity/trainer/verl/losses.py b/trinity/trainer/verl/losses.py index 2ab321fdc8b..4a9ebdb5b05 100644 --- a/trinity/trainer/verl/losses.py +++ b/trinity/trainer/verl/losses.py @@ -23,7 +23,11 @@ class TrinityPolicyLoss: workers via set_loss_fn(). """ - def __init__(self, algo_config: AlgorithmConfig): + def __init__( + self, + algo_config: AlgorithmConfig, + fix_actor_microbatch_loss_scale: bool = False, + ): self.policy_loss_fn = POLICY_LOSS_FN.get(algo_config.policy_loss_fn)( backend="verl", **algo_config.policy_loss_fn_args ) @@ -33,6 +37,7 @@ def __init__(self, algo_config: AlgorithmConfig): ) self.calculate_entropy = algo_config.entropy_loss_fn != "none" self.loss_agg_mode = algo_config.loss_agg_mode + self.fix_actor_microbatch_loss_scale = fix_actor_microbatch_loss_scale self.use_kl_loss = not isinstance(self.kl_loss_fn, DummyKLFn) def __call__( @@ -96,6 +101,18 @@ def __call__( # different scaling semantics. metrics["final_loss"] = policy_loss.detach().item() + # The custom loss bypasses veRL's global token normalization. + if ( + self.fix_actor_microbatch_loss_scale + and self.loss_agg_mode == "token-mean" + ): + policy_loss = ( + policy_loss + * response_mask.sum() + / data["batch_num_tokens"] + * data["dp_size"] + ) + return policy_loss, metrics def __repr__(self) -> str: @@ -106,6 +123,9 @@ def __repr__(self) -> str: ) -def build_trinity_loss(algo_config: AlgorithmConfig) -> TrinityPolicyLoss: +def build_trinity_loss( + algo_config: AlgorithmConfig, + fix_actor_microbatch_loss_scale: bool = False, +) -> TrinityPolicyLoss: """Build a TrinityPolicyLoss instance for veRL's engine API.""" - return TrinityPolicyLoss(algo_config) + return TrinityPolicyLoss(algo_config, fix_actor_microbatch_loss_scale) diff --git a/trinity/trainer/verl/trainer.py b/trinity/trainer/verl/trainer.py index 582786eebbc..3f18becbdd1 100644 --- a/trinity/trainer/verl/trainer.py +++ b/trinity/trainer/verl/trainer.py @@ -449,6 +449,9 @@ async def prepare(self): algo_config=self.algorithm_config, rollout_engine_type=self.global_config.explorer.rollout_model.engine_type, ray_namespace=self.global_config.synchronizer.ray_namespace, + fix_actor_microbatch_loss_scale=( + self.global_config.trainer.fix_actor_microbatch_loss_scale + ), ) self.global_steps = 0 self._load_checkpoint() diff --git a/trinity/trainer/verl/workers.py b/trinity/trainer/verl/workers.py index 6baa2967f0e..99e262dfbf3 100644 --- a/trinity/trainer/verl/workers.py +++ b/trinity/trainer/verl/workers.py @@ -263,19 +263,27 @@ def _cache_state_dict_meta(self): @register(dispatch_mode=Dispatch.ONE_TO_ALL) def set_trinity_config( - self, algo_config: AlgorithmConfig, rollout_engine_type: str, ray_namespace: str + self, + algo_config: AlgorithmConfig, + rollout_engine_type: str, + ray_namespace: str, + fix_actor_microbatch_loss_scale: bool, ): """Set Trinity-specific runtime config on the worker. This is called by VERLTrainer after worker initialization to inject: - The pluggable policy loss, KL loss, and entropy loss from Trinity's algorithm registry + - Token-based dynamic-microbatch loss scaling when enabled - The Ray namespace used to locate Synchronizer and CheckpointMonitor actors """ self._algo_config = algo_config self._ray_namespace = ray_namespace self._rollout_engine_type = rollout_engine_type if self.actor is not None: - loss_fn = build_trinity_loss(algo_config) + loss_fn = build_trinity_loss( + algo_config, + fix_actor_microbatch_loss_scale, + ) self.actor.set_loss_fn(loss_fn) @register(dispatch_mode=Dispatch.ONE_TO_ALL) diff --git a/trinity/trainer/verl_legacy/monkey_patch.py b/trinity/trainer/verl_legacy/monkey_patch.py index cb4c2a194dd..a2722acfc61 100644 --- a/trinity/trainer/verl_legacy/monkey_patch.py +++ b/trinity/trainer/verl_legacy/monkey_patch.py @@ -440,8 +440,10 @@ def state_dict(self, *args, **kwargs): from trinity.common.patch.qwen3_5 import ulysses_gate_delta_net_decorator - for layer in model.model.language_model.layers: - if layer.layer_type == "linear_attention": + language_model = model.model.language_model + for layer_idx, layer in enumerate(language_model.layers): + layer_type = language_model.config.layer_types[layer_idx] + if layer_type == "linear_attention": ulysses_gate_delta_net_decorator(layer.linear_attn, ulysses_sp_size) # Step 3: patch verl.utils.flops_counter diff --git a/trinity/trainer/verl_legacy/verl_config.py b/trinity/trainer/verl_legacy/verl_config.py index 2bf6c09ac01..463ecf8ed27 100644 --- a/trinity/trainer/verl_legacy/verl_config.py +++ b/trinity/trainer/verl_legacy/verl_config.py @@ -107,6 +107,7 @@ class FSDPConfig: wrap_policy: dict = field(default_factory=dict) fsdp_size: int = -1 forward_prefetch: bool = False + use_orig_params: bool = False model_dtype: Optional[str] = None dtype: str = "bfloat16" mixed_precision: dict = field(default_factory=dict)