Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion cosmos_framework/model/vfm/mot/unified_mot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import time
from collections.abc import Mapping
from dataclasses import dataclass
from pathlib import Path
from typing import Any

import torch
Expand Down Expand Up @@ -139,6 +140,29 @@ def is_moe(self) -> bool:
# MoT wrapper configs — one per architecture family
# -----------------------------------------------------------------------------

# Package root = parent of the top-level ``cosmos_framework`` package directory,
# i.e. the framework repo root in an editable install or site-packages for a
# wheel. The shipped model-config JSONs live under ``cosmos_framework/model/...``
# beneath it.
_PACKAGE_ROOT = Path(__file__).resolve().parents[4]


def _resolve_packaged_config_path(json_file: str) -> str:
"""Resolve a model-config JSON path so it loads regardless of CWD.

Absolute paths and paths that already exist relative to the CWD are returned
unchanged (preserving existing behavior when launched from the repo root).
A relative path that does not exist against the CWD — e.g. the shipped
``"cosmos_framework/model/.../X.json"`` defaults — is resolved against the
installed package root. If that candidate is missing too, the original path
is returned so ``open()`` raises the familiar ``FileNotFoundError``.
"""
path = Path(json_file)
if path.is_absolute() or path.exists():
return json_file
candidate = _PACKAGE_ROOT / json_file
return str(candidate) if candidate.exists() else json_file


class _MoTConfigBase(object):
"""Shared MoT wrapper logic for all three architecture families.
Expand Down Expand Up @@ -365,8 +389,16 @@ def from_json_file(cls, json_file: str) -> "_MoTConfigBase":
fields (when present) are surfaced lazily via
:pyattr:`vision_config` and by HF downstream consumers reading
the dict directly.

``json_file`` may be absolute, or relative. The shipped config
defaults reference these JSONs by a repo-root-relative path (e.g.
``"cosmos_framework/model/vfm/vlm/qwen3_vl/configs/X.json"``), which
only resolves when the process CWD is the framework repo root. To keep
``cosmos_framework.scripts.*`` runnable from any working directory, a
relative path that does not exist against the CWD is resolved against
the installed package root.
"""
with open(json_file, encoding="utf-8") as reader:
with open(_resolve_packaged_config_path(json_file), encoding="utf-8") as reader:
config_dict = json.load(reader)
return cls(config_dict=config_dict)

Expand Down
68 changes: 68 additions & 0 deletions cosmos_framework/model/vfm/mot/unified_mot_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: OpenMDW-1.1

"""Regression tests for package-relative model-config resolution.

The shipped config defaults (cosmos_framework/configs/base/defaults/vlm.py)
reference the per-architecture JSONs by a repo-root-relative path, e.g.
``"cosmos_framework/model/vfm/vlm/qwen3_vl/configs/Qwen3-VL-8B-Instruct.json"``.
``_MoTConfigBase.from_json_file`` used to ``open()`` that string verbatim, so it
only resolved when the process CWD was the framework repo root. Launching
``cosmos_framework.scripts.train`` from any other directory (e.g. a cookbook
folder) raised ``FileNotFoundError`` during model construction. These tests pin
the package-root fallback that fixes it.
"""

from pathlib import Path

import cosmos_framework
from cosmos_framework.model.vfm.mot.unified_mot import (
Qwen3VLMoTConfig,
_PACKAGE_ROOT,
_resolve_packaged_config_path,
)

# A config JSON that ships inside the package, named exactly as the config
# defaults reference it (relative to the package root).
_SHIPPED_REL = "cosmos_framework/model/vfm/vlm/qwen3_vl/configs/Qwen3-VL-8B-Instruct.json"


def test_package_root_contains_cosmos_framework():
# _PACKAGE_ROOT must be the directory that *contains* the cosmos_framework
# package, so that "<root>/cosmos_framework/..." resolves in both editable
# and wheel installs.
assert (_PACKAGE_ROOT / "cosmos_framework").is_dir()
assert Path(cosmos_framework.__file__).resolve().parent == _PACKAGE_ROOT / "cosmos_framework"


def test_resolve_absolute_path_passes_through(tmp_path):
abs_path = tmp_path / "cfg.json"
abs_path.write_text("{}")
assert _resolve_packaged_config_path(str(abs_path)) == str(abs_path)


def test_resolve_existing_relative_path_passes_through(tmp_path, monkeypatch):
# A relative path that exists against the CWD is returned unchanged — the
# package-root fallback must not shadow a real working-directory file.
monkeypatch.chdir(tmp_path)
(tmp_path / "local.json").write_text("{}")
assert _resolve_packaged_config_path("local.json") == "local.json"


def test_resolve_shipped_config_from_foreign_cwd(tmp_path, monkeypatch):
# The actual regression: from a directory that is not the repo root, the
# shipped repo-root-relative path still resolves to the packaged file.
monkeypatch.chdir(tmp_path)
resolved = Path(_resolve_packaged_config_path(_SHIPPED_REL))
assert resolved.is_absolute()
assert resolved.is_file()
assert resolved == _PACKAGE_ROOT / _SHIPPED_REL


def test_from_json_file_loads_shipped_config_from_foreign_cwd(tmp_path, monkeypatch):
# End-to-end: from_json_file (the call made during model construction) loads
# the shipped config from a foreign CWD instead of raising FileNotFoundError.
monkeypatch.chdir(tmp_path)
config = Qwen3VLMoTConfig.from_json_file(_SHIPPED_REL)
assert isinstance(config.config_dict, dict)
assert config.config_dict.get("model_type") # sanity: a real Qwen3-VL config