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
22 changes: 13 additions & 9 deletions test/backends/test_huggingface.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from mellea.stdlib.components import Intrinsic, Message
from mellea.stdlib.context import ChatContext, SimpleContext
from mellea.stdlib.requirements import ALoraRequirement, LLMaJRequirement
from test.conftest import hf_skip


@pytest.fixture(scope="module")
Expand All @@ -59,15 +60,18 @@ def backend():
against this granite-3.3 backend will fail once revision pinning is wired through
in phase-2.2 (#1141). Other intrinsics are not affected.
"""
backend = LocalHFBackend(
model_id=model_ids.IBM_GRANITE_4_1_3B, cache=SimpleLRUCache(5)
)
backend.add_adapter(
IntrinsicAdapter("requirement-check", base_model_name=backend.base_model_name)
)
backend.add_adapter(
IntrinsicAdapter("answerability", base_model_name=backend.base_model_name)
)
with hf_skip():
backend = LocalHFBackend(
model_id=model_ids.IBM_GRANITE_4_1_3B, cache=SimpleLRUCache(5)
)
backend.add_adapter(
IntrinsicAdapter(
"requirement-check", base_model_name=backend.base_model_name
)
)
backend.add_adapter(
IntrinsicAdapter("answerability", base_model_name=backend.base_model_name)
)
yield backend

from test.conftest import cleanup_gpu_backend
Expand Down
8 changes: 5 additions & 3 deletions test/backends/test_huggingface_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@
from mellea.backends.cache import SimpleLRUCache
from mellea.backends.huggingface import LocalHFBackend
from mellea.stdlib.context import ChatContext
from test.conftest import hf_skip


@pytest.fixture(scope="module")
def backend():
"""Shared HuggingFace backend for all tests in this module."""
backend = LocalHFBackend(
model_id=model_ids.MISTRALAI_MISTRAL_0_3_7B, cache=SimpleLRUCache(5)
)
with hf_skip():
backend = LocalHFBackend(
model_id=model_ids.MISTRALAI_MISTRAL_0_3_7B, cache=SimpleLRUCache(5)
)
# add_granite_aloras(backend)
yield backend

Expand Down
25 changes: 25 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
import gc
import os
import subprocess
Expand All @@ -9,6 +10,30 @@

from mellea.core import MelleaLogger

# ============================================================================
# HuggingFace Hub Skip Helper
# ============================================================================


@contextlib.contextmanager
def hf_skip():
"""Skip the current test/fixture when a HuggingFace Hub call fails.

Catches OSError (covers HfHubHTTPError, LocalEntryNotFoundError, and
transformers-wrapped Hub errors) plus requests network errors. Use this
in every fixture that downloads model weights or YAML files from the Hub
so a 429 or transient network blip results in a clean skip rather than an
ERROR that masks unrelated failures.
"""
try:
yield
except (OSError, requests.exceptions.RequestException) as e:
pytest.skip(
f"HuggingFace Hub not accessible: {type(e).__name__}: {e}",
allow_module_level=True,
)


# Try to import optional dependencies for system detection
try:
import psutil
Expand Down
10 changes: 3 additions & 7 deletions test/formatters/granite/test_intrinsics_formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

torch = pytest.importorskip("torch", reason="torch not installed — install mellea[hf]")
import yaml
from huggingface_hub.errors import LocalEntryNotFoundError

# First Party
from mellea.formatters.granite import (
Expand All @@ -32,6 +31,7 @@
)
from mellea.formatters.granite.base import util as base_util
from mellea.formatters.granite.intrinsics import json_util, util as intrinsics_util
from test.conftest import hf_skip


def _read_file(name):
Expand Down Expand Up @@ -147,15 +147,13 @@ def _resolve_yaml(self):
object. Called at fixture creation (execution time) to prevent collection time errors.
"""
if not self.yaml_file:
try:
with hf_skip():
self.yaml_file = intrinsics_util.obtain_io_yaml(
self.task,
self.base_model_id,
self.repo_id,
revision=self.revision, # type: ignore
)
except (LocalEntryNotFoundError, requests.exceptions.RequestException) as e:
pytest.skip(f"HuggingFace Hub not accessible: {type(e).__name__}: {e}")
return self


Expand Down Expand Up @@ -537,12 +535,10 @@ def test_read_yaml():
IntrinsicsRewriter(config_file=_INPUT_YAML_DIR / "answerability.yaml")

# Read from Hugging Face hub.
try:
with hf_skip():
local_path = intrinsics_util.obtain_io_yaml(
"answerability", "granite-4.0-micro", _RAG_INTRINSICS_REPO_NAME
)
except (LocalEntryNotFoundError, requests.exceptions.RequestException) as e:
pytest.skip(f"HuggingFace Hub not accessible: {type(e).__name__}: {e}")
IntrinsicsRewriter(config_file=local_path)


Expand Down
8 changes: 5 additions & 3 deletions test/stdlib/components/docs/test_richdocument.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import mellea
from mellea.core import TemplateRepresentation
from mellea.stdlib.components.docs.richdocument import RichDocument, Table
from test.conftest import hf_skip
from test.predicates import require_gpu

pytestmark = pytest.mark.integration
Expand All @@ -20,9 +21,10 @@
def rd() -> RichDocument:
# Use a specific document so we can test some of the functionality
# related to extracting and transforming text.
return RichDocument.from_document_file(
"https://arxiv.org/pdf/1906.04043", do_ocr=False
)
with hf_skip():
return RichDocument.from_document_file(
"https://arxiv.org/pdf/1906.04043", do_ocr=False
)


def test_richdocument_basics(rd: RichDocument):
Expand Down
5 changes: 3 additions & 2 deletions test/stdlib/components/intrinsic/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from mellea.stdlib.components.intrinsic import core
from mellea.stdlib.components.intrinsic._util import call_intrinsic
from mellea.stdlib.context import ChatContext
from test.conftest import cleanup_gpu_backend
from test.conftest import cleanup_gpu_backend, hf_skip
from test.predicates import require_gpu
from test.stdlib.components.intrinsic.test_rag import (
_read_input_json as _read_rag_input_json,
Expand Down Expand Up @@ -48,7 +48,8 @@ def _backend():
# Prevent thrashing if the default device is CPU
torch.set_num_threads(4)

backend_ = LocalHFBackend(model_id=BASE_MODEL)
with hf_skip():
backend_ = LocalHFBackend(model_id=BASE_MODEL)
yield backend_

# Code after yield is cleanup code.
Expand Down
5 changes: 3 additions & 2 deletions test/stdlib/components/intrinsic/test_guardian.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from mellea.stdlib.components import Document, Message
from mellea.stdlib.components.intrinsic import guardian
from mellea.stdlib.context import ChatContext
from test.conftest import cleanup_gpu_backend
from test.conftest import cleanup_gpu_backend, hf_skip
from test.predicates import require_gpu

# Skip entire module in CI since all tests are qualitative
Expand All @@ -37,7 +37,8 @@ def _backend():
"""Backend used by the tests in this file. Module-scoped to avoid reloading the model for each test."""
torch.set_num_threads(4)

backend_ = LocalHFBackend(model_id=IBM_GRANITE_4_MICRO_3B.hf_model_name) # type: ignore
with hf_skip():
backend_ = LocalHFBackend(model_id=IBM_GRANITE_4_MICRO_3B.hf_model_name) # type: ignore
yield backend_

cleanup_gpu_backend(backend_, "test_guardian")
Expand Down
7 changes: 5 additions & 2 deletions test/stdlib/components/intrinsic/test_rag.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from mellea.stdlib.components import Document, Message
from mellea.stdlib.components.intrinsic import rag
from mellea.stdlib.context import ChatContext
from test.conftest import hf_skip
from test.predicates import require_gpu

# Skip entire module in CI since all 7 tests are qualitative
Expand Down Expand Up @@ -42,7 +43,8 @@ def _backend():
torch.set_num_threads(4)

# No adapters for hybrid version.
backend_ = LocalHFBackend(model_id=IBM_GRANITE_4_1_3B.hf_model_name)
with hf_skip():
backend_ = LocalHFBackend(model_id=IBM_GRANITE_4_1_3B.hf_model_name)
yield backend_

from test.conftest import cleanup_gpu_backend
Expand All @@ -57,7 +59,8 @@ def _backend_4_0():
torch.set_num_threads(4)

# No adapters for hybrid version.
backend_ = LocalHFBackend(model_id=IBM_GRANITE_4_MICRO_3B.hf_model_name)
with hf_skip():
backend_ = LocalHFBackend(model_id=IBM_GRANITE_4_MICRO_3B.hf_model_name)
yield backend_

from test.conftest import cleanup_gpu_backend
Expand Down
4 changes: 3 additions & 1 deletion test/stdlib/requirements/test_groundedness_requirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
from mellea.stdlib.components import Document, Message
from mellea.stdlib.context import ChatContext
from mellea.stdlib.requirements.rag import GroundednessRequirement
from test.conftest import hf_skip
from test.predicates import require_gpu


@pytest.fixture
def backend():
"""Provide HuggingFace backend for tests."""
return LocalHFBackend(model_id="ibm-granite/granite-4.0-micro")
with hf_skip():
return LocalHFBackend(model_id="ibm-granite/granite-4.0-micro")


@pytest.fixture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
from mellea.stdlib.components import Document, Message
from mellea.stdlib.context import ChatContext
from mellea.stdlib.requirements.rag import GroundednessRequirement
from test.conftest import hf_skip
from test.predicates import require_gpu


@pytest.fixture(scope="session")
def backend():
"""Provide HuggingFace backend for tests (session-scoped to avoid OOM)."""
return LocalHFBackend(model_id="ibm-granite/granite-4.0-micro")
with hf_skip():
return LocalHFBackend(model_id="ibm-granite/granite-4.0-micro")


@pytest.fixture
Expand Down
12 changes: 7 additions & 5 deletions test/stdlib/test_spans.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from mellea.core import CBlock
from mellea.stdlib.components import SimpleComponent
from mellea.stdlib.session import MelleaSession, start_session
from test.conftest import hf_skip
from test.predicates import require_gpu

# Module-level markers for all tests using Granite 4.1 3B model
Expand All @@ -19,11 +20,12 @@
# We edit the context type in the async tests below. Don't change the scope here.
@pytest.fixture(scope="function")
def m_session(gh_run):
m = start_session(
"hf",
model_id=IBM_GRANITE_4_1_3B,
model_options={ModelOption.MAX_NEW_TOKENS: 64},
)
with hf_skip():
m = start_session(
"hf",
model_id=IBM_GRANITE_4_1_3B,
model_options={ModelOption.MAX_NEW_TOKENS: 64},
)
yield m

from test.conftest import cleanup_gpu_backend
Expand Down
10 changes: 6 additions & 4 deletions test/telemetry/test_metrics_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
)
from mellea.stdlib.components import Message
from mellea.stdlib.context import SimpleContext
from test.conftest import hf_skip
from test.predicates import require_api_key, require_gpu

# Check if OpenTelemetry is available
Expand Down Expand Up @@ -72,10 +73,11 @@ def hf_metrics_backend(gh_run):
from mellea.backends.cache import SimpleLRUCache
from mellea.backends.huggingface import LocalHFBackend

backend = LocalHFBackend(
model_id=IBM_GRANITE_4_1_3B.hf_model_name, # type: ignore
cache=SimpleLRUCache(5),
)
with hf_skip():
backend = LocalHFBackend(
model_id=IBM_GRANITE_4_1_3B.hf_model_name, # type: ignore
cache=SimpleLRUCache(5),
)

yield backend

Expand Down
Loading