Skip to content
Open
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
37 changes: 27 additions & 10 deletions STYLEGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,24 @@ This project uses lazy loading for heavy third-party dependencies to optimize im
**Heavy third-party libraries** (>100ms import cost) should be lazy-loaded via `lazy_heavy_imports.py`:

```python
# Don't import directly
import pandas as pd
import numpy as np
from __future__ import annotations

# Use lazy loading with IDE support
# Don't import directly:
# import pandas as pd
# import numpy as np

# Use lazy loading with IDE support:
from typing import TYPE_CHECKING
from data_designer.lazy_heavy_imports import pd, np

import data_designer.lazy_heavy_imports as lazy

if TYPE_CHECKING:
import pandas as pd
import numpy as np
import pandas as pd


def to_frame(values: np.ndarray) -> pd.DataFrame:
return lazy.pd.DataFrame({"x": values})
```

This pattern provides:
Expand All @@ -172,11 +179,18 @@ If you add a new dependency with significant import cost (>100ms):

2. **Update imports across codebase:**
```python
from __future__ import annotations

from typing import TYPE_CHECKING
from data_designer.lazy_heavy_imports import your_lib

import data_designer.lazy_heavy_imports as lazy

if TYPE_CHECKING:
import your_library_name as your_lib


def summarize(model: your_lib.Model) -> str:
return lazy.your_lib.summarize(model)
```

3. **Verify with performance test:**
Expand All @@ -191,7 +205,7 @@ If you add a new dependency with significant import cost (>100ms):
**DO put in TYPE_CHECKING:**
- Internal `data_designer` imports used **only** in type hints
- Imports that would cause circular dependencies
- Full imports of lazy-loaded libraries for IDE support (e.g., `import pandas as pd` in addition to runtime `from data_designer.lazy_heavy_imports import pd`)
- Full imports of lazy-loaded libraries for IDE support (e.g., `import pandas as pd` in addition to runtime `import data_designer.lazy_heavy_imports as lazy`)

**DON'T put in TYPE_CHECKING:**
- Standard library imports (`Path`, `Any`, `Callable`, `Literal`, `TypeAlias`, etc.)
Expand All @@ -202,15 +216,18 @@ If you add a new dependency with significant import cost (>100ms):
**Examples:**

```python
from __future__ import annotations

# CORRECT - Lazy-loaded library with IDE support
from typing import TYPE_CHECKING
from data_designer.lazy_heavy_imports import pd

import data_designer.lazy_heavy_imports as lazy

if TYPE_CHECKING:
import pandas as pd

def load_data(path: str) -> pd.DataFrame:
return pd.read_csv(path)
return lazy.pd.read_csv(path)

# CORRECT - Standard library NOT in TYPE_CHECKING
from pathlib import Path
Expand Down
12 changes: 12 additions & 0 deletions packages/data-designer/tests/test_lazy_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,15 @@ def test_runtime_src_avoids_from_lazy_heavy_imports_pattern() -> None:
offenders.append(str(path.relative_to(repo_root)))

assert not offenders, f"Runtime source files should avoid from-import lazy pattern: {offenders}"


def test_scripts_avoid_from_lazy_heavy_imports_pattern() -> None:
repo_root = Path(__file__).resolve().parents[3]
pattern = re.compile(r"^\s*from\s+data_designer\.lazy_heavy_imports\s+import\b", re.MULTILINE)
offenders: list[str] = []

for path in sorted((repo_root / "scripts").glob("**/*.py")):
if pattern.search(path.read_text()):
offenders.append(str(path.relative_to(repo_root)))

assert not offenders, f"Scripts should avoid from-import lazy pattern: {offenders}"
17 changes: 8 additions & 9 deletions scripts/benchmarks/benchmark_engine_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from pathlib import Path
from typing import TYPE_CHECKING, Any

import data_designer.lazy_heavy_imports as lazy
from data_designer.config.column_configs import LLMTextColumnConfig, SamplerColumnConfig, ValidationColumnConfig
from data_designer.config.config_builder import DataDesignerConfigBuilder
from data_designer.config.mcp import MCPProvider, ToolConfig
Expand All @@ -31,10 +32,8 @@
from data_designer.config.validator_params import LocalCallableValidatorParams, ValidatorType
from data_designer.engine.mcp.registry import MCPToolDefinition, MCPToolResult
from data_designer.engine.models.clients.types import AssistantMessage, ChatCompletionResponse, ToolCall
from data_designer.lazy_heavy_imports import np, pd

if TYPE_CHECKING:
import numpy as np
import pandas as pd


Expand Down Expand Up @@ -277,11 +276,11 @@ def _format_stats(stats: MetricStats, *, unit: str, precision: int = 3) -> str:


def _json_default(value: Any) -> Any:
if isinstance(value, np.generic):
if isinstance(value, lazy.np.generic):
return value.item()
if isinstance(value, np.ndarray):
if isinstance(value, lazy.np.ndarray):
return value.tolist()
if isinstance(value, (pd.Timestamp, pd.Timedelta)):
if isinstance(value, (lazy.pd.Timestamp, lazy.pd.Timedelta)):
return value.isoformat()
if isinstance(value, set):
return sorted(value)
Expand Down Expand Up @@ -472,10 +471,10 @@ def _validate_recommendation(df: pd.DataFrame) -> pd.DataFrame:
series = df["llm_stage3"].astype(str)
scores = series.map(lambda text: _extract_metric(text, "score"))
latencies = series.map(lambda text: _extract_metric(text, "latency_ms"))
scores_numeric = pd.to_numeric(scores, errors="coerce")
latency_numeric = pd.to_numeric(latencies, errors="coerce")
scores_numeric = lazy.pd.to_numeric(scores, errors="coerce")
latency_numeric = lazy.pd.to_numeric(latencies, errors="coerce")
is_valid = scores_numeric.between(0.0, 10.0) & latency_numeric.between(0.0, 900.0)
return pd.DataFrame(
return lazy.pd.DataFrame(
{
"is_valid": is_valid.fillna(False).astype(bool),
"score": scores_numeric,
Expand Down Expand Up @@ -584,7 +583,7 @@ def _run_single_benchmark(settings: BenchmarkSettings, engine_mode: str) -> Benc
from data_designer.engine.storage.artifact_storage import ArtifactStorage

random.seed(settings.seed)
np.random.seed(settings.seed)
lazy.np.random.seed(settings.seed)

run_config = RunConfig(
buffer_size=settings.buffer_size,
Expand Down
Loading