Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python | |
| """Prompt-based LLM/static-prior runner for BrainRL. | |
| Runs one (or more) full episodes through the OpenEnv environment with the | |
| selected condition / subject context. With ``--use-llm`` it asks a chat model | |
| for one JSON action per OpenEnv step; without, it uses the deterministic | |
| prompt fallback so the demo always works. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| from pathlib import Path | |
| from data_split import DEFAULT_PARTICIPANT_INFO, build_condition_split | |
| from prompts import build_action_prompt, llm_prompt_policy_action, static_prompt_policy_action | |
| from server.brain_environment import BrainRegionSelectionEnvironment | |
| def build_parser() -> argparse.ArgumentParser: | |
| parser = argparse.ArgumentParser(description="Run prompt-based inference on BrainRL") | |
| parser.add_argument("--use-llm", action="store_true", help="Ask an LLM for each action") | |
| parser.add_argument("--show-prompts", action="store_true", help="Print prompt every step") | |
| parser.add_argument("--seed", type=int, default=42) | |
| parser.add_argument("--episodes", type=int, default=1, help="How many episodes to roll out") | |
| parser.add_argument( | |
| "--condition", | |
| choices=("single_m", "single_f", "mixed_m", "mixed_f"), | |
| default=None, | |
| ) | |
| parser.add_argument( | |
| "--participant-info", | |
| type=str, | |
| default=str(DEFAULT_PARTICIPANT_INFO), | |
| ) | |
| parser.add_argument("--train-subjects", type=str, default=None) | |
| parser.add_argument("--test-subjects", type=str, default=None) | |
| parser.add_argument( | |
| "--exclude-subjects", | |
| type=str, | |
| default=None, | |
| help="Comma list / range of subjects to drop (e.g. corrupted recordings).", | |
| ) | |
| parser.add_argument( | |
| "--split", | |
| choices=("train", "test", "all"), | |
| default="all", | |
| ) | |
| parser.add_argument("--subject-id", type=str, default=None, help="Override single subject id") | |
| parser.add_argument("--run-id", type=str, default=None, help="Override single run id (run1..run4)") | |
| return parser | |
| def _episode_pairs(args: argparse.Namespace) -> list[dict[str, str | None]]: | |
| if args.subject_id: | |
| return [ | |
| { | |
| "subject_id": args.subject_id, | |
| "run_id": args.run_id, | |
| "condition": args.condition, | |
| } | |
| ] | |
| if args.condition is None: | |
| return [{"subject_id": None, "run_id": None, "condition": None}] | |
| split = build_condition_split( | |
| condition=args.condition, | |
| participant_info_path=args.participant_info, | |
| train_subjects_spec=args.train_subjects, | |
| test_subjects_spec=args.test_subjects, | |
| exclude_subjects=args.exclude_subjects, | |
| ) | |
| pairs = split.pairs_for(args.split) | |
| if not pairs: | |
| raise SystemExit(f"No pairs for {args.condition}/{args.split}.") | |
| return [pair.as_dict() for pair in pairs] | |
| def run_one_episode(env: BrainRegionSelectionEnvironment, args: argparse.Namespace) -> None: | |
| stim = env._stimulus_features | |
| stim_label = ( | |
| f" stimulus=window={stim['window_index'] + 1}/{stim['n_windows']} " | |
| f"dominant_pos={stim.get('dominant_pos')} n_words={stim.get('n_words')} " | |
| f"density={stim.get('speech_density'):.2f}" | |
| if stim | |
| else " stimulus=unavailable" | |
| ) | |
| print( | |
| f"[INIT] subject={env._subject_id} run={env._run_id} condition={env._condition} " | |
| f"candidates={env._subset.n_regions} budget={env._subset.selection_budget} " | |
| f"prompt_top_k={env._subset.prompt_top_k}" + stim_label | |
| ) | |
| rewards: list[float] = [] | |
| done = False | |
| while not done: | |
| state = env._build_selection_state() | |
| if args.show_prompts: | |
| print("[PROMPT]") | |
| print(build_action_prompt(state)) | |
| if args.use_llm: | |
| region_id, raw_text = llm_prompt_policy_action(state) | |
| else: | |
| region_id = static_prompt_policy_action(state) | |
| raw_text = f'{{"region_id": "{region_id}"}}' | |
| result = env._process_action(region_id) | |
| rewards.append(float(result["reward"])) | |
| print( | |
| f"[STEP {env._timestep}/{env._subset.selection_budget}] region={region_id} " | |
| f"reward={float(result['reward']):.4f} r2={float(result['current_r2']):.4f} " | |
| f"done={result['done']}" | |
| ) | |
| if args.use_llm: | |
| print(f"[MODEL] {raw_text}") | |
| done = bool(result["done"]) | |
| print( | |
| f"[END] final_r2={env._current_r2:.4f} total_reward={sum(rewards):.4f} " | |
| f"selected={len(env._selected_region_ids)} " | |
| f"order={' -> '.join(env._selected_region_ids[:10])}" | |
| + (" ..." if len(env._selected_region_ids) > 10 else "") | |
| ) | |
| def main() -> None: | |
| args = build_parser().parse_args() | |
| pairs = _episode_pairs(args) | |
| env = BrainRegionSelectionEnvironment() | |
| for episode_idx in range(int(args.episodes)): | |
| pair = pairs[episode_idx % len(pairs)] | |
| env.reset( | |
| seed=int(args.seed) + episode_idx, | |
| subject_id=pair.get("subject_id"), | |
| run_id=pair.get("run_id"), | |
| condition=pair.get("condition"), | |
| ) | |
| if int(args.episodes) > 1: | |
| print(f"\n=== Episode {episode_idx + 1}/{args.episodes} ===") | |
| run_one_episode(env, args) | |
| if __name__ == "__main__": | |
| main() | |