diff --git a/.github/workflows/vr-foraging-cicd.yml b/.github/workflows/vr-foraging-cicd.yml index b4230030..c606518b 100644 --- a/.github/workflows/vr-foraging-cicd.yml +++ b/.github/workflows/vr-foraging-cicd.yml @@ -303,7 +303,9 @@ jobs: uses: actions/checkout@v5 - name: Install uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true - name: Setup Graphviz uses: ts-graphviz/setup-graphviz@v2 diff --git a/docs/conf.py b/docs/conf.py index 98653809..323ce512 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -90,5 +90,5 @@ def export_model_diagram(model: BaseModel, root: str = _static_path) -> None: # -- Dataset rendering -with open(f"{_static_path}/dataset.txt", "w", encoding="utf-8") as f: +with open(f"{_static_path}/dataset.html", "w", encoding="utf-8") as f: f.write(contract.render_dataset()) diff --git a/docs/dataset.rst b/docs/dataset.rst index 3355dbbe..ee6fe789 100644 --- a/docs/dataset.rst +++ b/docs/dataset.rst @@ -1,6 +1,10 @@ Data Contract ------------- -.. literalinclude:: /_static/dataset.txt - :language: text - :caption: Dataset \ No newline at end of file +.. raw:: html + + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index d05d3f5a..f2c258ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ Changelog = "https://github.com/AllenNeuralDynamics/Aind.Behavior.VrForaging/rel data = ["contraqctor<0.6.0"] launcher = [ - "aind-clabe[aind-services]>=0.7", + "aind-clabe[aind-services] >= 0.8.0 ,<0.9.0", "aind-data-schema>=2", "aind_behavior_vr_foraging[data]", ] diff --git a/src/aind_behavior_vr_foraging/curricula/aind.Behavior.VrForaging.Curricula b/src/aind_behavior_vr_foraging/curricula/aind.Behavior.VrForaging.Curricula index f86e99a1..ce50dd48 160000 --- a/src/aind_behavior_vr_foraging/curricula/aind.Behavior.VrForaging.Curricula +++ b/src/aind_behavior_vr_foraging/curricula/aind.Behavior.VrForaging.Curricula @@ -1 +1 @@ -Subproject commit f86e99a1276a16dd02b9566e6a39bdd5f7223dc0 +Subproject commit ce50dd48ebe883fab1ea7a2dbce27050d0dc55e0 diff --git a/src/aind_behavior_vr_foraging/data_contract/__init__.py b/src/aind_behavior_vr_foraging/data_contract/__init__.py index 4a323ab9..6b1ae308 100644 --- a/src/aind_behavior_vr_foraging/data_contract/__init__.py +++ b/src/aind_behavior_vr_foraging/data_contract/__init__.py @@ -41,7 +41,9 @@ def dataset(path: os.PathLike, version: str = __semver__) -> contraqctor.contrac def render_dataset(version: str = __semver__) -> str: """Renders the dataset as a tree-like structure for visualization.""" - from contraqctor.contract.utils import print_data_stream_tree + from contraqctor.contract.utils import print_data_stream_tree_html dataset_constructor = _dataset_lookup_helper(version) - return print_data_stream_tree(dataset_constructor(Path("")), show_missing_indicator=False, show_type=True) + return print_data_stream_tree_html( + dataset_constructor(Path("")), show_missing_indicator=False, show_type=True + ) diff --git a/src/aind_behavior_vr_foraging/data_mappers/__init__.py b/src/aind_behavior_vr_foraging/data_mappers/__init__.py index 5ccfeaf2..1b01923d 100644 --- a/src/aind_behavior_vr_foraging/data_mappers/__init__.py +++ b/src/aind_behavior_vr_foraging/data_mappers/__init__.py @@ -46,8 +46,8 @@ def cli_cmd(self): repo = Repo(self.repo_path) session_mapped = AindSessionDataMapper( - session_model=session, - rig_model=rig, + session=session, + rig=rig, task_logic_model=task_logic, repository=repo, script_path=Path("./src/main.bonsai"), diff --git a/src/aind_behavior_vr_foraging/data_mappers/_rig.py b/src/aind_behavior_vr_foraging/data_mappers/_rig.py index 3243beff..bf6413e0 100644 --- a/src/aind_behavior_vr_foraging/data_mappers/_rig.py +++ b/src/aind_behavior_vr_foraging/data_mappers/_rig.py @@ -4,7 +4,7 @@ import platform from decimal import Decimal from pathlib import Path -from typing import Any, Callable, Optional, cast +from typing import Optional, cast import aind_behavior_services.rig as AbsRig from aind_behavior_services import calibration as AbsCalibration @@ -14,7 +14,6 @@ from aind_data_schema_models import coordinates as aind_schema_model_coordinates from aind_data_schema_models import modalities, units from clabe.data_mapper import aind_data_schema as ads -from clabe.launcher import Launcher from aind_behavior_vr_foraging.rig import AindVrForagingRig @@ -45,10 +44,10 @@ def get_spawned_device(self, name: str) -> devices.Device: class AindRigDataMapper(ads.AindDataSchemaRigDataMapper): def __init__( self, - rig_model: AindVrForagingRig, + rig: AindVrForagingRig, ): super().__init__() - self.rig_model = rig_model + self.rig_model = rig self._mapped: Optional[instrument.Instrument] = None def rig_schema(self): @@ -75,17 +74,6 @@ def mapped(self) -> instrument.Instrument: def is_mapped(self) -> bool: return self.mapped is not None - @classmethod - def build_runner(cls) -> Callable[[Launcher[AindVrForagingRig, Any, Any]], "AindRigDataMapper"]: - def _new( - launcher: Launcher[AindVrForagingRig, Any, Any], - ) -> "AindRigDataMapper": - new = cls(rig_model=launcher.get_rig(strict=True)) - new.map() - return new - - return _new - ## From here on, private methods only! ## Lasciate ogne speranza, voi ch'entrate diff --git a/src/aind_behavior_vr_foraging/data_mappers/_session.py b/src/aind_behavior_vr_foraging/data_mappers/_session.py index 68c56b62..530cfe42 100644 --- a/src/aind_behavior_vr_foraging/data_mappers/_session.py +++ b/src/aind_behavior_vr_foraging/data_mappers/_session.py @@ -3,7 +3,7 @@ import os import sys from pathlib import Path -from typing import Callable, Dict, List, Optional, Union +from typing import List, Optional, Union import aind_behavior_services.rig as AbsRig import git @@ -17,7 +17,6 @@ from clabe.apps import CurriculumSuggestion from clabe.data_mapper import aind_data_schema as ads from clabe.data_mapper import helpers as data_mapper_helpers -from clabe.launcher import Launcher from aind_behavior_vr_foraging.rig import AindVrForagingRig from aind_behavior_vr_foraging.task_logic import AindVrForagingTaskLogic @@ -30,24 +29,22 @@ class AindSessionDataMapper(ads.AindDataSchemaSessionDataMapper): def __init__( self, - session_model: AindBehaviorSessionModel, - rig_model: AindVrForagingRig, - task_logic_model: AindVrForagingTaskLogic, + session: AindBehaviorSessionModel, + rig: AindVrForagingRig, + task_logic: AindVrForagingTaskLogic, repository: Union[os.PathLike, git.Repo] = Path("."), script_path: os.PathLike = Path("./src/main.bonsai"), session_end_time: Optional[datetime.datetime] = None, curriculum_suggestion: Optional[CurriculumSuggestion] = None, - output_parameters: Optional[Dict] = None, ): - self.session_model = session_model - self.rig_model = rig_model - self.task_logic_model = task_logic_model + self.session_model = session + self.rig_model = rig + self.task_logic_model = task_logic self.repository = repository if isinstance(self.repository, os.PathLike | str): self.repository = git.Repo(Path(self.repository)) self.script_path = script_path self._session_end_time = session_end_time - self.output_parameters = output_parameters self._mapped: Optional[acquisition.Acquisition] = None self.curriculum = curriculum_suggestion @@ -60,28 +57,6 @@ def session_end_time(self) -> datetime.datetime: def session_schema(self): return self.mapped - @classmethod - def build_runner( - cls, - curriculum_suggestion: Optional[Callable[[], CurriculumSuggestion | None]] = None, - ) -> Callable[ - [Launcher[AindVrForagingRig, AindBehaviorSessionModel, AindVrForagingTaskLogic]], "AindSessionDataMapper" - ]: - def _new( - launcher: Launcher[AindVrForagingRig, AindBehaviorSessionModel, AindVrForagingTaskLogic], - ) -> "AindSessionDataMapper": - new = cls( - session_model=launcher.get_session(strict=True), - rig_model=launcher.get_rig(strict=True), - task_logic_model=launcher.get_task_logic(strict=True), - repository=launcher.repository, - curriculum_suggestion=curriculum_suggestion() if curriculum_suggestion is not None else None, - ) - new.map() - return new - - return _new - @property def session_name(self) -> str: if self.session_model.session_name is None: @@ -97,7 +72,7 @@ def mapped(self) -> acquisition.Acquisition: def is_mapped(self) -> bool: return self.mapped is not None - def map(self) -> Optional[acquisition.Acquisition]: + def map(self) -> acquisition.Acquisition: logger.info("Mapping aind-data-schema Session.") try: self._mapped = self._map() @@ -120,7 +95,7 @@ def _map(self) -> acquisition.Acquisition: acquisition_end_time=utcnow(), acquisition_start_time=self.session_model.date, experimenters=self.session_model.experimenter, - acquisition_type=self.session_model.experiment, + acquisition_type=self.session_model.experiment or self.task_logic_model.name, coordinate_system=_make_origin_coordinate_system(), data_streams=self._get_data_streams(), calibrations=self._get_calibrations(), @@ -245,7 +220,7 @@ def _get_stimulus_epochs(self) -> List[acquisition.StimulusEpoch]: stimulus_start_time=self.session_model.date, stimulus_end_time=self.session_end_time, configurations=stimulus_epoch_configurations, - stimulus_name=self.session_model.experiment, + stimulus_name=self.session_model.experiment or self.task_logic_model.name, stimulus_modalities=stimulus_modalities, performance_metrics=performance_metrics, curriculum_status=curriculum_status, diff --git a/src/aind_behavior_vr_foraging/data_mappers/_utils.py b/src/aind_behavior_vr_foraging/data_mappers/_utils.py index 38a024ca..60a713aa 100644 --- a/src/aind_behavior_vr_foraging/data_mappers/_utils.py +++ b/src/aind_behavior_vr_foraging/data_mappers/_utils.py @@ -1,7 +1,6 @@ import enum import logging -from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, List, Optional, Type, TypeVar, Union +from typing import List, Optional, Type, TypeVar, Union import aind_behavior_services.calibration as AbsCalibration import pydantic @@ -9,16 +8,9 @@ from aind_data_schema.components import coordinates, measurements from aind_data_schema.core import acquisition from aind_data_schema_models import units -from clabe.launcher import Launcher, Promise from aind_behavior_vr_foraging.rig import AindVrForagingRig -if TYPE_CHECKING: - from ._rig import AindRigDataMapper - from ._session import AindSessionDataMapper -else: - AindRigDataMapper = AindSessionDataMapper = Any - TTo = TypeVar("TTo", bound=pydantic.BaseModel) T = TypeVar("T") @@ -38,22 +30,6 @@ def coerce_to_aind_data_schema(value: Union[pydantic.BaseModel, dict], target_ty return target_type(**_normalized_input) -def write_ads_mappers( - session_mapper: Promise[[Launcher], AindSessionDataMapper], rig_mapper: Promise[[Launcher], AindRigDataMapper] -) -> Callable[[Launcher], None]: - def _run(launcher: Launcher) -> None: - session_directory = launcher.session_directory - _session = session_mapper.result.mapped - _rig = rig_mapper.result.mapped - _session.instrument_id = _rig.instrument_id - logger.info("Writing session.json to %s", session_directory) - _session.write_standard_file(Path(session_directory)) - logger.info("Writing rig.json to %s", session_directory) - _rig.write_standard_file(Path(session_directory)) - - return _run - - def _get_water_calibration(rig_model: AindVrForagingRig) -> List[measurements.VolumeCalibration]: def _mapper( device_name: Optional[str], water_calibration: AbsCalibration.water_valve.WaterValveCalibration diff --git a/src/aind_behavior_vr_foraging/launcher.py b/src/aind_behavior_vr_foraging/launcher.py index 753eb334..524f7ebb 100644 --- a/src/aind_behavior_vr_foraging/launcher.py +++ b/src/aind_behavior_vr_foraging/launcher.py @@ -1,7 +1,7 @@ +import logging from pathlib import Path from typing import Any, cast -from aind_behavior_curriculum import TrainerState from aind_behavior_services.calibration.aind_manipulator import ManipulatorPosition from aind_behavior_services.session import AindBehaviorSessionModel from clabe import resource_monitor @@ -13,7 +13,7 @@ CurriculumSuggestion, ) from clabe.data_transfer.aind_watchdog import WatchdogDataTransferService, WatchdogSettings -from clabe.launcher import Launcher, LauncherCliArgs, MaybeResult, Promise, run_if +from clabe.launcher import Launcher, LauncherCliArgs from clabe.pickers import DefaultBehaviorPickerSettings from clabe.pickers.dataverse import DataversePicker from contraqctor.contract.json import SoftwareEvents @@ -21,84 +21,85 @@ from . import data_contract from .data_mappers import AindRigDataMapper, AindSessionDataMapper -from .data_mappers._utils import write_ads_mappers from .rig import AindVrForagingRig from .task_logic import AindVrForagingTaskLogic +logger = logging.getLogger(__name__) -def make_launcher(settings: LauncherCliArgs) -> Launcher: + +def experiment(launcher: Launcher) -> None: monitor = resource_monitor.ResourceMonitor( constrains=[ - resource_monitor.available_storage_constraint_factory(settings.data_dir, 2e11), + resource_monitor.available_storage_constraint_factory(launcher.settings.data_dir, 2e11), ] ) - bonsai_app = AindBehaviorServicesBonsaiApp(BonsaiAppSettings(workflow=Path(r"./src/main.bonsai"))) - trainer = CurriculumApp(settings=CurriculumSettings()) - watchdog_settings = WatchdogSettings() # type: ignore[call-arg] - picker = DataversePicker[AindVrForagingRig, AindBehaviorSessionModel, AindVrForagingTaskLogic]( - settings=DefaultBehaviorPickerSettings() - ) - launcher = Launcher( - rig=AindVrForagingRig, - session=AindBehaviorSessionModel, - task_logic=AindVrForagingTaskLogic, - settings=settings, - ) - manipulator_modifier = ByAnimalManipulatorModifier(picker) - # Get user input - launcher.register_callable(picker.initialize) - launcher.register_callable(picker.pick_session) - launcher.register_callable(picker.pick_rig) - launcher.register_callable(manipulator_modifier.inject) - launcher.register_callable(picker.pick_trainer_state) + # Validate resources + monitor.run() + + # Start experiment setup + picker = DataversePicker(launcher=launcher, settings=DefaultBehaviorPickerSettings()) + manipulator_modifier = ByAnimalManipulatorModifier(picker, launcher) + + # Pick and register session + session = picker.pick_session(AindBehaviorSessionModel) + launcher.register_session(session) + + # Fetch the task settings + trainer_state, task_logic = picker.pick_trainer_state(AindVrForagingTaskLogic) + input_trainer_state_path = launcher.save_temp_model(trainer_state) - # Check resources - launcher.register_callable(monitor.build_runner()) + # Fetch rig settings + rig = picker.pick_rig(AindVrForagingRig) + + # Post-fetching modifications + manipulator_modifier.inject(rig) # Run the task via Bonsai - launcher.register_callable(bonsai_app.build_runner(allow_std_error=True)) + bonsai_app = AindBehaviorServicesBonsaiApp(BonsaiAppSettings(workflow=Path(r"./src/main.bonsai"))) + bonsai_app.add_app_settings(launcher, rig=rig, session=session, task_logic=task_logic) + bonsai_app.run().get_result(allow_stderr=True) # Update manipulator initial position for next session - launcher.register_callable(manipulator_modifier.dump) + manipulator_modifier.dump() # Curriculum - suggestion = launcher.register_callable( - run_if(lambda: trainer_state_exists_predicate(picker.trainer_state))( - trainer.build_runner(input_trainer_state=lambda: picker.trainer_state, allow_std_error=True) - ) - ) - - launcher.register_callable( - run_if(lambda: suggestion.result.has_result())( - lambda launcher: _dump_suggestion(launcher, suggestion.result.result) - ) - ) - - launcher.register_callable( - run_if(lambda: suggestion.result.has_result())( - lambda launcher: picker.push_new_suggestion(launcher, suggestion.result.result.trainer_state) + suggestion: CurriculumSuggestion | None = None + if not ( + (picker.trainer_state is None) + or (picker.trainer_state.is_on_curriculum is False) + or (picker.trainer_state.stage is None) + ): + trainer = CurriculumApp( + settings=CurriculumSettings( + input_trainer_state=input_trainer_state_path, data_directory=launcher.session_directory + ) ) - ) + # Run the curriculum + suggestion = trainer.run().get_result(allow_stderr=True) + # Dump suggestion for debugging (for now, but we will prob remove this later) + _dump_suggestion(launcher, suggestion) + # Push updated trainer state back to the database + picker.push_new_suggestion(suggestion.trainer_state) # Mappers - session_mapper_promise = launcher.register_callable( - AindSessionDataMapper.build_runner(curriculum_suggestion=lambda: _resolve_trainer_suggestion(suggestion)) - ) - rig_mapper_promise = launcher.register_callable(AindRigDataMapper.build_runner()) - launcher.register_callable(write_ads_mappers(session_mapper_promise, rig_mapper_promise)) + ads_session = AindSessionDataMapper( + rig=rig, session=session, task_logic=task_logic, curriculum_suggestion=suggestion + ).map() + ads_session.write_standard_file(launcher.session_directory) + ads_rig = AindRigDataMapper(rig=rig).map() + ads_rig.write_standard_file(launcher.session_directory) # Watchdog - launcher.register_callable(lambda x: x.copy_logs()) - launcher.register_callable( - WatchdogDataTransferService.build_runner( - settings=watchdog_settings, aind_session_data_mapper=session_mapper_promise.as_callable() - ) - ) - return launcher + launcher.copy_logs() + WatchdogDataTransferService( + source=launcher.session_directory, settings=WatchdogSettings(), aind_data_schema_session=ads_session + ).transfer() + + return -def _dump_suggestion(launcher: Launcher[Any, Any, Any], suggestion: CurriculumSuggestion) -> None: +def _dump_suggestion(launcher: Launcher, suggestion: CurriculumSuggestion) -> None: launcher.logger.info( f"Dumping curriculum suggestion to: {launcher.session_directory / 'Behavior' / 'Logs' / 'suggestion.json'}" ) @@ -106,42 +107,28 @@ def _dump_suggestion(launcher: Launcher[Any, Any, Any], suggestion: CurriculumSu f.write(suggestion.model_dump_json(indent=2)) -def _resolve_trainer_suggestion( - input_trainer_state: Promise[Any, MaybeResult[CurriculumSuggestion]], -) -> CurriculumSuggestion | None: - if not input_trainer_state.has_result(): - return None - if not input_trainer_state.result.has_result(): - return None - return input_trainer_state.result.result - - class ByAnimalManipulatorModifier: - def __init__(self, picker: DataversePicker) -> None: + def __init__(self, picker: DataversePicker, launcher: Launcher) -> None: self._picker = picker + self._launcher = launcher - def inject(self, launcher: Launcher[AindVrForagingRig, Any, Any]) -> None: - rig = launcher.get_rig(strict=True) - if launcher.subject is None: - raise ValueError("Launcher subject is not defined!") - target_folder = self._picker.subject_dir / launcher.subject + def inject(self, rig: AindVrForagingRig) -> AindVrForagingRig: + subject = self._launcher.session.subject + target_folder = self._picker.subject_dir / subject target_file = target_folder / "manipulator_init.json" if not target_file.exists(): - launcher.logger.warning(f"Manipulator initial position file not found: {target_file}. Using default.") - return + logger.warning(f"Manipulator initial position file not found: {target_file}. Using default.") else: cached = ManipulatorPosition.model_validate_json(target_file.read_text(encoding="utf-8")) - launcher.logger.info(f"Loading manipulator initial position from: {target_file}. Deserialized: {cached}") + logger.info(f"Loading manipulator initial position from: {target_file}. Deserialized: {cached}") assert rig.manipulator.calibration is not None rig.manipulator.calibration.input.initial_position = cached - launcher.set_rig(rig, force=True) - return + return rig - def dump(self, launcher: Launcher[AindVrForagingRig, Any, Any]) -> None: - assert launcher.subject is not None - target_folder = self._picker.subject_dir / launcher.subject + def dump(self) -> None: + target_folder = self._picker.subject_dir / self._launcher.session.subject target_file = target_folder / "manipulator_init.json" - _dataset = data_contract.dataset(launcher.session_directory) + _dataset = data_contract.dataset(self._launcher.session_directory) try: manipulator_parking_position: SoftwareEvents = cast( SoftwareEvents, _dataset["Behavior"]["SoftwareEvents"]["SpoutParkingPositions"].load() @@ -149,30 +136,18 @@ def dump(self, launcher: Launcher[AindVrForagingRig, Any, Any]) -> None: data: dict[str, Any] = manipulator_parking_position.data.iloc[0]["data"]["ResetPosition"] position = ManipulatorPosition.model_validate(data) except Exception as e: - launcher.logger.error(f"Failed to load manipulator parking position: {e}") + logger.error(f"Failed to load manipulator parking position: {e}") return else: - launcher.logger.info(f"Saving manipulator initial position to: {target_file}. Serialized: {position}") + logger.info(f"Saving manipulator initial position to: {target_file}. Serialized: {position}") target_folder.mkdir(parents=True, exist_ok=True) target_file.write_text(position.model_dump_json(indent=2), encoding="utf-8") -def trainer_state_exists_predicate(input_trainer_state: TrainerState | Promise[Any, TrainerState]) -> bool: - if isinstance(input_trainer_state, Promise): - input_trainer_state = input_trainer_state.result - if input_trainer_state is None: - return False - if input_trainer_state.is_on_curriculum is False: - return False - if input_trainer_state.stage is None: - return False - return True - - class ClabeCli(LauncherCliArgs): def cli_cmd(self): - launcher = make_launcher(self) - launcher.main() + launcher = Launcher(settings=self) + launcher.run_experiment(experiment) return None diff --git a/tests/test_aind_data_mapper.py b/tests/test_aind_data_mapper.py index 7bdbd29c..5f2f3723 100644 --- a/tests/test_aind_data_mapper.py +++ b/tests/test_aind_data_mapper.py @@ -18,18 +18,18 @@ class TestAindSessionDataMapper(unittest.TestCase): def setUp(self): - self.session_model = session - self.rig_model = rig - self.task_logic_model = task_logic + self.session = session + self.rig = rig + self.task_logic = task_logic self.repository = Repo(Path("./")) self.script_path = Path("./src/main.bonsai") self.session_end_time = datetime.now() self.session_directory = None self.mapper = AindSessionDataMapper( - session_model=self.session_model, - rig_model=self.rig_model, - task_logic_model=self.task_logic_model, + session=self.session, + rig=self.rig, + task_logic=self.task_logic, repository=self.repository, script_path=self.script_path, session_end_time=self.session_end_time, @@ -54,9 +54,9 @@ def test_round_trip(self): class TestAindRigDataMapper(unittest.TestCase): def setUp(self): - self.rig_model = rig + self.rig = rig self.mapper = AindRigDataMapper( - rig_model=self.rig_model, + rig=self.rig, ) @patch("aind_behavior_vr_foraging.data_mappers.AindRigDataMapper._map") @@ -78,16 +78,16 @@ def test_round_trip(self): class TestInstrumentAcquisitionCompatibility(unittest.TestCase): def setUp(self): - self.rig_model = rig - self.session_model = session - self.task_logic_model = task_logic + self.rig = rig + self.session = session + self.task_logic = task_logic self.rig_mapper = AindRigDataMapper( - rig_model=self.rig_model, + rig=self.rig, ) self.session_mapper = AindSessionDataMapper( - session_model=self.session_model, - rig_model=self.rig_model, - task_logic_model=self.task_logic_model, + session=self.session, + rig=self.rig, + task_logic=self.task_logic, repository=Repo(Path("./")), script_path=Path("./src/main.bonsai"), session_end_time=datetime.now(), diff --git a/uv.lock b/uv.lock index 81bfba33..9168eda2 100644 --- a/uv.lock +++ b/uv.lock @@ -94,7 +94,7 @@ docs = [ requires-dist = [ { name = "aind-behavior-services", specifier = ">=0.12,<0.13" }, { name = "aind-behavior-vr-foraging", extras = ["data"], marker = "extra == 'launcher'" }, - { name = "aind-clabe", extras = ["aind-services"], marker = "extra == 'launcher'", specifier = ">=0.7" }, + { name = "aind-clabe", extras = ["aind-services"], marker = "extra == 'launcher'", specifier = ">=0.8.0,<0.9.0" }, { name = "aind-data-schema", marker = "extra == 'launcher'", specifier = ">=2" }, { name = "contraqctor", marker = "extra == 'data'", specifier = "<0.6.0" }, { name = "pydantic-settings" }, @@ -120,7 +120,7 @@ docs = [ [[package]] name = "aind-clabe" -version = "0.7.2" +version = "0.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aind-behavior-services" }, @@ -130,9 +130,9 @@ dependencies = [ { name = "rich" }, { name = "semver" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/55/8f40de12cbc1512b6288d12ccb2cc0eb68c8b8d16ecc1341fd11f6f3c617/aind_clabe-0.7.2.tar.gz", hash = "sha256:efc05686b04dda88671f388e68756096355811dba3281d2a7f325a164a491bce", size = 57005, upload-time = "2025-10-14T21:50:08.119Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/a8/3f32138efe3a3b986d615b2745c845c835010c579de4d9cedd785836209f/aind_clabe-0.8.0.tar.gz", hash = "sha256:ba5ae6a53d12887781072a9422cf9313fc97ad296ebbc805229ae3c76e6b3b09", size = 47572, upload-time = "2025-10-19T21:50:06.393Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/84/427ee953299442c2c88ef558b82fee258ce60c9b939311edf4bc2179a6cb/aind_clabe-0.7.2-py3-none-any.whl", hash = "sha256:fa17dfb49a7cbeba6f79d017c8d255e05597ae5406adbb70006683706271c0c9", size = 75560, upload-time = "2025-10-14T21:50:06.942Z" }, + { url = "https://files.pythonhosted.org/packages/ad/30/c9e291decb3afd9b9b109a3205e83c7b2e2bee978e94c00d291c51268550/aind_clabe-0.8.0-py3-none-any.whl", hash = "sha256:a011cb5295a0f9b9079062794ac98a946310b93c3c7ef91c860bc3d57b1dc167", size = 64794, upload-time = "2025-10-19T21:50:05.042Z" }, ] [package.optional-dependencies]