diff --git a/dimos/core/global_config.py b/dimos/core/global_config.py index 42a7fa552a..90461932a2 100644 --- a/dimos/core/global_config.py +++ b/dimos/core/global_config.py @@ -17,7 +17,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict -from dimos.models.vl.create import VlModelName +from dimos.models.vl.types import VlModelName ViewerBackend: TypeAlias = Literal["rerun", "rerun-web", "rerun-connect", "foxglove", "none"] diff --git a/dimos/mapping/occupancy/path_map.py b/dimos/mapping/occupancy/path_map.py index 8920c6e30b..69a1f93738 100644 --- a/dimos/mapping/occupancy/path_map.py +++ b/dimos/mapping/occupancy/path_map.py @@ -12,15 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Literal, TypeAlias - from dimos.mapping.occupancy.gradient import GradientStrategy, gradient, voronoi_gradient from dimos.mapping.occupancy.inflation import simple_inflate from dimos.mapping.occupancy.operations import overlay_occupied, smooth_occupied +from dimos.mapping.occupancy.types import NavigationStrategy from dimos.msgs.nav_msgs.OccupancyGrid import OccupancyGrid -NavigationStrategy: TypeAlias = Literal["simple", "mixed"] - def make_navigation_map( occupancy_grid: OccupancyGrid, diff --git a/dimos/mapping/occupancy/types.py b/dimos/mapping/occupancy/types.py new file mode 100644 index 0000000000..87f2084698 --- /dev/null +++ b/dimos/mapping/occupancy/types.py @@ -0,0 +1,17 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Literal, TypeAlias + +NavigationStrategy: TypeAlias = Literal["simple", "mixed"] diff --git a/dimos/models/vl/create.py b/dimos/models/vl/create.py index 6c778d4104..7fe5a0dcb2 100644 --- a/dimos/models/vl/create.py +++ b/dimos/models/vl/create.py @@ -1,9 +1,8 @@ -from typing import Any, Literal +from typing import Any +from dimos.models.vl.types import VlModelName from dimos.models.vl.base import VlModel -VlModelName = Literal["qwen", "moondream"] - def create(name: VlModelName) -> VlModel[Any]: # This uses inline imports to only import what's needed. diff --git a/dimos/models/vl/types.py b/dimos/models/vl/types.py new file mode 100644 index 0000000000..ac8b0f024d --- /dev/null +++ b/dimos/models/vl/types.py @@ -0,0 +1,3 @@ +from typing import Literal + +VlModelName = Literal["qwen", "moondream"] diff --git a/dimos/robot/cli/test_cli_startup.py b/dimos/robot/cli/test_cli_startup.py new file mode 100644 index 0000000000..aa9886c24e --- /dev/null +++ b/dimos/robot/cli/test_cli_startup.py @@ -0,0 +1,63 @@ +# Copyright 2025-2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Guard against import-time regressions in the CLI entrypoint. + +`dimos --help` should never pull in heavy ML/viz libraries. If it does, +startup time balloons from <2s to >5s, which is a terrible UX. +""" + +import subprocess +import sys +import time + +# CI runners are slower — give generous headroom but still catch gross regressions. +HELP_TIMEOUT_SECONDS = 8 + + +def test_help_does_not_import_heavy_deps() -> None: + """GlobalConfig import must not drag in matplotlib, torch, or scipy.""" + result = subprocess.run( + [ + sys.executable, + "-c", + ( + "import sys; " + "from dimos.core.global_config import GlobalConfig; " + "bad = [m for m in ('matplotlib', 'torch', 'scipy') if m in sys.modules]; " + "assert not bad, f'Heavy deps imported: {bad}'" + ), + ], + capture_output=True, + text=True, + timeout=30, + ) + assert result.returncode == 0, f"Heavy deps leaked into GlobalConfig import:\n{result.stderr}" + + +def test_help_startup_time() -> None: + """`dimos --help` must finish in under {HELP_TIMEOUT_SECONDS}s.""" + start = time.monotonic() + result = subprocess.run( + [sys.executable, "-m", "dimos.robot.cli.dimos", "--help"], + capture_output=True, + text=True, + timeout=HELP_TIMEOUT_SECONDS + 5, # hard kill safety margin + ) + elapsed = time.monotonic() - start + assert result.returncode == 0, f"dimos --help failed:\n{result.stderr}" + assert elapsed < HELP_TIMEOUT_SECONDS, ( + f"dimos --help took {elapsed:.1f}s (limit: {HELP_TIMEOUT_SECONDS}s). " + f"Check for heavy imports in the CLI entrypoint or GlobalConfig." + )