diff --git a/eval_protocol/training/__init__.py b/eval_protocol/training/__init__.py index 122b6a7a..b8338144 100644 --- a/eval_protocol/training/__init__.py +++ b/eval_protocol/training/__init__.py @@ -1,11 +1,30 @@ -from .gepa_trainer import GEPATrainer -from .gepa_utils import ( - DSPyModuleType, - DSPyModuleFactory, - create_single_turn_program, - create_signature, - build_reflection_lm, -) +from typing import TYPE_CHECKING + +# GEPA/DSPy-related imports are optional - only available when dspy extra is installed +# Use: pip install eval-protocol[dspy] +_DSPY_AVAILABLE = False +try: + import dspy # noqa: F401 + + _DSPY_AVAILABLE = True +except ImportError: + pass + + +def _raise_dspy_import_error(name: str): + """Raise a helpful error when dspy is not installed.""" + raise ImportError(f"'{name}' requires the 'dspy' extra. Install it with: pip install eval-protocol[dspy]") + + +if TYPE_CHECKING or _DSPY_AVAILABLE: + from .gepa_trainer import GEPATrainer + from .gepa_utils import ( + DSPyModuleType, + DSPyModuleFactory, + create_single_turn_program, + create_signature, + build_reflection_lm, + ) __all__ = [ "GEPATrainer", @@ -17,3 +36,10 @@ # Reflection LM helpers "build_reflection_lm", ] + + +def __getattr__(name: str): + """Lazy loading for dspy-dependent exports.""" + if name in __all__ and not _DSPY_AVAILABLE: + _raise_dspy_import_error(name) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/pyproject.toml b/pyproject.toml index 97a41aeb..400e8d40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,6 @@ dependencies = [ "deepdiff>=6.0.0", "websockets>=15.0.1", "fastapi>=0.116.1", - "dspy>=3.0.0", "fireworks-ai==1.0.0a18", ] @@ -132,6 +131,9 @@ braintrust = [ openenv = [ "openenv-core", ] +dspy = [ + "dspy>=3.0.0", +] # Optional deps for LangGraph example/tests langgraph = [ diff --git a/uv.lock b/uv.lock index 75b38223..972f90b1 100644 --- a/uv.lock +++ b/uv.lock @@ -1163,7 +1163,6 @@ dependencies = [ { name = "dataclasses-json" }, { name = "deepdiff" }, { name = "docstring-parser" }, - { name = "dspy" }, { name = "fastapi" }, { name = "fireworks-ai" }, { name = "httpx" }, @@ -1230,6 +1229,9 @@ dev = [ { name = "versioneer" }, { name = "werkzeug" }, ] +dspy = [ + { name = "dspy" }, +] huggingface = [ { name = "datasets" }, { name = "transformers" }, @@ -1304,7 +1306,7 @@ requires-dist = [ { name = "deepdiff", specifier = ">=6.0.0" }, { name = "docker", marker = "extra == 'dev'", specifier = "==7.1.0" }, { name = "docstring-parser", specifier = ">=0.15" }, - { name = "dspy", specifier = ">=3.0.0" }, + { name = "dspy", marker = "extra == 'dspy'", specifier = ">=3.0.0" }, { name = "e2b", marker = "extra == 'dev'" }, { name = "fastapi", specifier = ">=0.116.1" }, { name = "fireworks-ai", specifier = "==1.0.0a18" }, @@ -1377,7 +1379,7 @@ requires-dist = [ { name = "websockets", specifier = ">=15.0.1" }, { name = "werkzeug", marker = "extra == 'dev'", specifier = ">=2.0.0" }, ] -provides-extras = ["dev", "trl", "openevals", "box2d", "langfuse", "huggingface", "langsmith", "bigquery", "svgbench", "pydantic", "supabase", "chinook", "langchain", "braintrust", "openenv", "langgraph", "langgraph-tools", "proxy"] +provides-extras = ["dev", "trl", "openevals", "box2d", "langfuse", "huggingface", "langsmith", "bigquery", "svgbench", "pydantic", "supabase", "chinook", "langchain", "braintrust", "openenv", "dspy", "langgraph", "langgraph-tools", "proxy"] [package.metadata.requires-dev] dev = [