Compare commits

..

2 Commits

Author SHA1 Message Date
infra-runtime-be 1eb44301e2 fix(workspace/test): isolate test_load_config_basic and test_load_config_defaults from MODEL_PROVIDER env
sop-tier-check / tier-check (pull_request) Failing after 2s
Secret scan / Scan diff for credential-shaped strings (pull_request) Failing after 5s
audit-force-merge / audit (pull_request) Has been skipped
Issue #271: when MODEL_PROVIDER=minimax is set in the environment, these two tests
fail because load_config reads MODEL_PROVIDER and overrides the model, causing:
- test_load_config_basic: expects "openai:gpt-4o", gets "minimax"
- test_load_config_defaults: expects "anthropic:claude-opus-4-7", gets "minimax"

The fix adds monkeypatch.delenv("MODEL_PROVIDER", raising=False) to both tests,
making them deterministic regardless of the host environment. Consistent with
other tests in the file that already isolate this env var (e.g.
test_runtime_config_model_falls_back_to_top_level, test_runtime_config_model_yaml_wins).

Closes: https://git.moleculesai.app/molecule-ai/molecule-core/issues/271

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 12:11:13 +00:00
infra-runtime-be 2f5dca1c61 fix(workspace): register plugins_registry as sys.modules shim before loading adapters
Secret scan / Scan diff for credential-shaped strings (pull_request) Failing after 15s
sop-tier-check / tier-check (pull_request) Failing after 1s
KI-296 fix: when the PyPI-installed runtime (molecule-ai-workspace-runtime
0.1.129+) ships plugins_registry as molecule_runtime.plugins_registry (a
subpackage), plugin adapter files that do ``from plugins_registry import ...``
as a top-level name fail with ModuleNotFoundError because Python's import
system cannot find a top-level ``plugins_registry`` package.

The fix in plugins_registry/__init__.py:_load_module_from_path() registers
molecule_runtime.plugins_registry as ``plugins_registry`` in sys.modules
before exec'ing any plugin adapter file, so the top-level import resolves
correctly in both environments:
- PyPI wheel (molecule_runtime.plugins_registry → sys.modules["plugins_registry"])
- molecule-core workspace source (top-level workspace/plugins_registry already
  on sys.path; the setdefault is a no-op)

Submodules (builtins, protocol, raw_drop) are also registered so adapters
that import ``from plugins_registry.builtins import ...`` work without error.

Added test_load_module_from_path_registers_plugins_registry_sys_modules.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 12:02:56 +00:00
3 changed files with 71 additions and 2 deletions
+26
View File
@@ -51,6 +51,32 @@ class AdaptorSource:
def _load_module_from_path(module_name: str, path: Path):
"""Import a Python file by absolute path. Returns the module or None on failure."""
# KI-296: Before exec'ing plugin-adapter files (which import
# ``from plugins_registry import ...`` as a top-level name), register
# the molecule-runtime subpackage as ``plugins_registry`` in sys.modules.
# In the molecule-core workspace source this is already a top-level package,
# so the setdefault is a no-op. In the PyPI-installed runtime wheel
# (molecule-ai-workspace-runtime 0.1.129+), the package ships as
# ``molecule_runtime.plugins_registry`` and without this shim every
# plugin adapter would fail with ModuleNotFoundError.
import sys as _sys
if "plugins_registry" not in _sys.modules:
try:
_mr_pr = __import__("molecule_runtime.plugins_registry", fromlist=[""])
_sys.modules["plugins_registry"] = _mr_pr
# Also register submodules the adapters commonly import directly.
for _sub in ("builtins", "protocol", "raw_drop"):
_submod = getattr(_mr_pr, _sub, None)
if _submod is not None:
_sys.modules[f"plugins_registry.{_sub}"] = _submod
except ImportError:
# molecule-runtime not installed (e.g. test environment with
# workspace/ on sys.path directly) — skip shim; the top-level
# workspace/plugins_registry package is already findable.
pass
spec = importlib.util.spec_from_file_location(module_name, path)
if spec is None or spec.loader is None:
return None
+4 -2
View File
@@ -17,8 +17,9 @@ from config import (
)
def test_load_config_basic(tmp_path):
def test_load_config_basic(tmp_path, monkeypatch):
"""load_config reads a YAML file and returns a WorkspaceConfig."""
monkeypatch.delenv("MODEL_PROVIDER", raising=False)
config_yaml = tmp_path / "config.yaml"
config_yaml.write_text(
yaml.dump(
@@ -46,8 +47,9 @@ def test_load_config_basic(tmp_path):
assert cfg.prompt_files == ["SOUL.md", "TOOLS.md"]
def test_load_config_defaults(tmp_path):
def test_load_config_defaults(tmp_path, monkeypatch):
"""Missing fields fall back to WorkspaceConfig defaults."""
monkeypatch.delenv("MODEL_PROVIDER", raising=False)
config_yaml = tmp_path / "config.yaml"
config_yaml.write_text(yaml.dump({}))
+41
View File
@@ -325,3 +325,44 @@ def test_resolve_registry_missing_module_falls_through(monkeypatch, tmp_path: Pa
monkeypatch.setattr(pr, "_REGISTRY_ROOT", tmp_path / "empty-registry")
_, source = pr.resolve("demo-plugin", "test_runtime", plugin_root)
assert source == AdaptorSource.RAW_DROP
def test_load_module_from_path_registers_plugins_registry_sys_modules(tmp_path: Path):
"""KI-296: _load_module_from_path registers ``plugins_registry`` in sys.modules
before exec'ing the adapter, so adapter files that do
``from plugins_registry import ...`` resolve correctly when the runtime is
installed from the PyPI wheel (where the package ships as
``molecule_runtime.plugins_registry`` rather than a top-level ``plugins_registry``).
"""
import sys as _sys
import plugins_registry as pr
# Create a fake adapter that imports plugins_registry at top level.
adapter_file = tmp_path / "fake_runtime_adapter.py"
adapter_file.write_text(
"from plugins_registry import InstallContext # noqa: F401\n"
"from plugins_registry.builtins import AgentskillsAdaptor as Adaptor # noqa: F401\n"
)
# Evict any pre-existing sys.modules entries for the shim keys so the
# import inside _load_module_from_path actually runs.
_saved = {
k: _sys.modules.pop(k, None)
for k in (
"plugins_registry", "plugins_registry.builtins",
"plugins_registry.protocol", "plugins_registry.raw_drop",
"_plugin_adaptor.test.fake_runtime",
)
}
try:
result = pr._load_module_from_path("_plugin_adaptor.test.fake_runtime", adapter_file)
assert result is not None, "module should load without ImportError"
assert hasattr(result, "Adaptor"), "AgentskillsAdaptor alias should be in namespace"
finally:
# Restore sys.modules state.
for k, v in _saved.items():
if v is None:
_sys.modules.pop(k, None)
else:
_sys.modules[k] = v