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
18 changes: 12 additions & 6 deletions .pipelines/templates/build-python-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ parameters:
default: false
- name: flcWheelsDir
type: string
default: ''
displayName: 'Path to directory containing the FLC wheels (for overriding foundry-local-core)'
- name: outputDir
type: string
Expand Down Expand Up @@ -111,16 +110,23 @@ steps:
Write-Warning "No FLC wheel found matching $filter in ${{ parameters.flcWheelsDir }}"
}

- script: pip install onnxruntime-core==1.24.3 onnxruntime-genai-core==0.12.1
displayName: 'Install ORT native packages'

- script: pip install "pydantic>=2.0.0" "requests>=2.32.4" "openai>=2.24.0"
displayName: 'Install pure python dependencies'

# Build wheel — standard or WinML variant
# skip-native-deps=true omits foundry-local-core/onnxruntime pinned versions
# from the wheel metadata, since the pipeline pre-installs its own builds.
# The wheel retains all dependencies in its metadata so end users get
# native packages installed automatically. CI uses --no-deps to avoid
# re-downloading packages that were pre-installed from pipeline builds.
- ${{ if eq(parameters.isWinML, true) }}:
- script: python -m build --wheel -C winml=true -C skip-native-deps=true --outdir dist/
- script: python -m build --wheel -C winml=true --outdir dist/
displayName: 'Build wheel (WinML)'
workingDirectory: $(repoRoot)/sdk/python

- ${{ else }}:
- script: python -m build --wheel -C skip-native-deps=true --outdir dist/
- script: python -m build --wheel --outdir dist/
displayName: 'Build wheel'
workingDirectory: $(repoRoot)/sdk/python

Expand All @@ -131,7 +137,7 @@ steps:
targetType: inline
script: |
$wheel = (Get-ChildItem "$(repoRoot)/sdk/python/dist/*.whl" | Select-Object -First 1).FullName
pip install $wheel
pip install --no-deps $wheel

# Stage output
- task: PowerShell@2
Expand Down
15 changes: 7 additions & 8 deletions .pipelines/templates/test-python-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ parameters:
default: false
- name: flcWheelsDir
type: string
default: ''
displayName: 'Path to directory containing the FLC wheels'

steps:
Expand Down Expand Up @@ -99,19 +98,19 @@ steps:
Write-Warning "No FLC wheel found matching $filter"
}

# Install ORT native packages from the ORT-Nightly feed.
# skip-native-deps strips these from the SDK wheel metadata, so they
# must be installed explicitly for tests to locate the native binaries.
- script: pip install onnxruntime-core onnxruntime-genai-core
- script: pip install onnxruntime-core==1.24.3 onnxruntime-genai-core==0.12.1
displayName: 'Install ORT native packages'

- script: pip install "pydantic>=2.0.0" "requests>=2.32.4" "openai>=2.24.0"
displayName: 'Install pure python dependencies'

- ${{ if not(parameters.isWinML) }}:
- script: python -m build --wheel -C skip-native-deps=true --outdir dist/
- script: python -m build --wheel --outdir dist/
displayName: 'Build wheel'
workingDirectory: $(repoRoot)/sdk/python

- ${{ if parameters.isWinML }}:
- script: python -m build --wheel -C winml=true -C skip-native-deps=true --outdir dist/
- script: python -m build --wheel -C winml=true --outdir dist/
displayName: 'Build wheel (WinML)'
workingDirectory: $(repoRoot)/sdk/python

Expand All @@ -121,7 +120,7 @@ steps:
targetType: inline
script: |
$wheel = (Get-ChildItem "$(repoRoot)/sdk/python/dist/*.whl" | Select-Object -First 1).FullName
pip install $wheel
pip install --no-deps $wheel

- script: pip install coverage pytest>=7.0.0 pytest-timeout>=2.1.0
displayName: 'Install test dependencies'
Expand Down
76 changes: 7 additions & 69 deletions sdk/python/build_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@

python -m build --wheel -C winml=true

Skip native deps (use pre-installed foundry-local-core / ORT / GenAI)::

python -m build --wheel -C skip-native-deps=true

Environment variable fallback (useful in CI pipelines)::

FOUNDRY_VARIANT=winml python -m build --wheel
FOUNDRY_SKIP_NATIVE_DEPS=1 python -m build --wheel

CI usage (install without pulling dependencies)::

pip install --no-deps <wheel>
"""

from __future__ import annotations
Expand All @@ -51,13 +50,6 @@
_STANDARD_NAME = 'name = "foundry-local-sdk"'
_WINML_NAME = 'name = "foundry-local-sdk-winml"'

# Native binary package prefixes to strip when skip-native-deps is active.
_NATIVE_DEP_PREFIXES = (
"foundry-local-core",
"onnxruntime-core",
"onnxruntime-genai-core",
)


# ---------------------------------------------------------------------------
# Variant detection
Expand All @@ -75,23 +67,6 @@ def _is_winml(config_settings: dict | None) -> bool:
return os.environ.get("FOUNDRY_VARIANT", "").lower() == "winml"


def _is_skip_native_deps(config_settings: dict | None) -> bool:
"""Return True when native binary dependencies should be omitted.

When set, ``foundry-local-core``, ``onnxruntime-core``, and
``onnxruntime-genai-core`` are stripped from requirements.txt so the
wheel is built against whatever versions are already installed.
Useful in CI pipelines that pre-install pipeline-built native wheels.

Checks ``config_settings["skip-native-deps"]`` first
(set via ``-C skip-native-deps=true``), then falls back to the
``FOUNDRY_SKIP_NATIVE_DEPS`` environment variable.
"""
if config_settings and str(config_settings.get("skip-native-deps", "")).lower() == "true":
return True
return os.environ.get("FOUNDRY_SKIP_NATIVE_DEPS", "").lower() in ("1", "true")


# ---------------------------------------------------------------------------
# In-place patching context manager
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -125,48 +100,11 @@ def _patch_for_winml() -> Generator[None, None, None]:
_REQUIREMENTS.write_text(requirements_original, encoding="utf-8")


@contextlib.contextmanager
def _strip_native_deps() -> Generator[None, None, None]:
"""Temporarily remove native binary deps from requirements.txt.

Lines starting with any prefix in ``_NATIVE_DEP_PREFIXES`` (case-
insensitive) are removed. The file is restored in the ``finally``
block.
"""
requirements_original = _REQUIREMENTS.read_text(encoding="utf-8")
try:
filtered = [
line for line in requirements_original.splitlines(keepends=True)
if not any(line.lstrip().lower().startswith(p) for p in _NATIVE_DEP_PREFIXES)
]
_REQUIREMENTS.write_text("".join(filtered), encoding="utf-8")
yield
finally:
_REQUIREMENTS.write_text(requirements_original, encoding="utf-8")


def _apply_patches(config_settings: dict | None):
"""Return a context manager that applies the appropriate patches."""
winml = _is_winml(config_settings)
skip_native = _is_skip_native_deps(config_settings)

@contextlib.contextmanager
def _combined():
# Stack contexts: WinML swaps requirements first, then strip_native
# removes native deps from whatever requirements are active.
if winml and skip_native:
with _patch_for_winml(), _strip_native_deps():
yield
elif winml:
with _patch_for_winml():
yield
elif skip_native:
with _strip_native_deps():
yield
else:
yield

return _combined()
if _is_winml(config_settings):
return _patch_for_winml()
return contextlib.nullcontext()


# ---------------------------------------------------------------------------
Expand Down
Loading