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
105 changes: 42 additions & 63 deletions .github/workflows/samples-integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,48 +36,40 @@ jobs:
with:
python-version: '3.12'

- name: Configure pip for Azure Artifacts
- name: Configure pip for Azure Artifacts (ORT-Nightly feed for onnxruntime deps)
run: |
pip config set global.index-url https://pkgs.dev.azure.com/aiinfra/PublicPackages/_packaging/ORT-Nightly/pypi/simple/
pip config set global.extra-index-url https://pypi.org/simple/
pip config set global.pre true

- name: Build and install SDK from source
working-directory: sdk/python
shell: pwsh
run: |
python -m pip install build
echo '__version__ = "0.0.0-dev"' > src/version.py
python -m build --wheel --outdir dist/
$wheel = (Get-ChildItem dist/*.whl | Select-Object -First 1).FullName
pip install $wheel

- name: Install sample dependencies
shell: pwsh
run: |
Get-ChildItem samples/python/*/requirements.txt -ErrorAction SilentlyContinue | ForEach-Object {
Write-Host "Installing dependencies for $($_.Directory.Name)..."
pip install -r $_.FullName
}

- name: Syntax check Python samples
# Samples consume the SDK from local source (tracking main) via an editable
# install declared in each sample's requirements.txt: `-e ../../../sdk/python`.
# We install from inside each sample directory so that relative path resolves.
- name: Install samples (SDK from local source) and syntax check
shell: pwsh
run: |
$failed = @()
$samples = Get-ChildItem samples/python/*/src/app.py -ErrorAction SilentlyContinue
foreach ($sample in $samples) {
$name = $sample.Directory.Parent.Name
Write-Host "=== Checking: $name ==="
python -m py_compile $sample.FullName
$samples = Get-ChildItem samples/python -Directory -ErrorAction SilentlyContinue
foreach ($dir in $samples) {
$app = Join-Path $dir.FullName 'src/app.py'
if (-not (Test-Path $app)) { continue }
Write-Host "=== $($dir.Name) ==="
Push-Location $dir.FullName
if (Test-Path requirements.txt) {
pip install -r requirements.txt
if ($LASTEXITCODE -ne 0) { Write-Host "INSTALL FAILED: $($dir.Name)"; $failed += $dir.Name; Pop-Location; continue }
}
Pop-Location
python -m py_compile $app
if ($LASTEXITCODE -ne 0) {
Write-Host "FAILED: $name"
$failed += $name
Write-Host "FAILED: $($dir.Name)"
$failed += $dir.Name
} else {
Write-Host "OK: $name"
Write-Host "OK: $($dir.Name)"
}
}
if ($failed.Count -gt 0) {
Write-Error "Failed syntax checks: $($failed -join ', ')"
Write-Error "Failed: $($failed -join ', ')"
exit 1
}

Expand Down Expand Up @@ -134,26 +126,24 @@ jobs:
run: |
npm install
npm run build
npm link

- name: Syntax check JS samples
# Samples consume the SDK from local source (tracking main) via a
# `file:../../../sdk/js` dependency in each sample's package.json, so a plain
# `npm install` inside the sample resolves the locally built SDK (no npm link).
- name: Install and syntax check JS samples
shell: pwsh
run: |
$failed = @()
# Find all sample app.js files (either in root or src/)
$samples = @()
$samples += Get-ChildItem samples/js/*/app.js -ErrorAction SilentlyContinue
$samples += Get-ChildItem samples/js/*/src/app.js -ErrorAction SilentlyContinue
foreach ($sample in $samples) {
$dir = if ($sample.Directory.Name -eq 'src') { $sample.Directory.Parent } else { $sample.Directory }
$name = $dir.Name
Write-Host "=== Checking: $name ==="
# Link SDK and install dependencies
Push-Location $dir.FullName
npm link foundry-local-sdk 2>$null
if (Test-Path "package.json") { npm install 2>$null }
if (Test-Path "package.json") { npm install }
Pop-Location
# Syntax check
node --check $sample.FullName 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "FAILED: $name"
Expand Down Expand Up @@ -188,35 +178,13 @@ jobs:
8.0.x
10.0.x

- name: Build SDK from source
shell: pwsh
run: |
# Build cross-platform SDK package
# Note: /p:TreatWarningsAsErrors=false avoids failing on SDK doc warnings
dotnet pack sdk/cs/src/Microsoft.AI.Foundry.Local.csproj `
-o local-packages `
/p:Version=0.9.0-dev `
/p:IsPacking=true `
/p:TreatWarningsAsErrors=false `
--configuration Release

# Build WinML SDK package (Windows only)
if ($IsWindows) {
dotnet pack sdk/cs/src/Microsoft.AI.Foundry.Local.csproj `
-o local-packages `
/p:Version=0.9.0-dev-20260324 `
/p:UseWinML=true `
/p:IsPacking=true `
/p:TreatWarningsAsErrors=false `
--configuration Release
}

Write-Host "Local packages:"
Get-ChildItem local-packages/*.nupkg | ForEach-Object { Write-Host " $($_.Name)" }

- name: Build C# samples
- name: Build C# samples (SDK via ProjectReference to sdk/cs source)
shell: pwsh
run: |
# Samples consume the SDK from local source (tracking main) via a
# ProjectReference to sdk/cs/src/Microsoft.AI.Foundry.Local.csproj.
# Building each sample compiles the SDK from source and restores its
# transitive Microsoft.AI.Foundry.Local.Core + third-party packages from nuget.org.
$failed = @()
$projects = Get-ChildItem samples/cs -Recurse -Filter "*.csproj"
foreach ($proj in $projects) {
Expand Down Expand Up @@ -282,3 +250,14 @@ jobs:
- name: Clippy check
working-directory: samples/rust
run: cargo clippy --workspace -- -D warnings

# ── C++ Samples ─────────────────────────────────────────────────────
# Intentionally not built here. The C++ samples consume the C++ SDK
# (sdk_v2/cpp), which is built only on dedicated build agents with vcpkg + ONNX
# Runtime provisioned (see .pipelines/) — there is no GitHub-hosted-runner recipe,
# and building it on every samples PR would be slow and flaky. The samples
# reference a *locally built* SDK unambiguously via cmake/FoundryLocalSDK.cmake,
# which fails fast with clear guidance if the SDK has not been built. Validate
# locally after `python sdk_v2/cpp/build.py`:
# cmake -S samples/cpp/<sample> -B samples/cpp/<sample>/build
# cmake --build samples/cpp/<sample>/build
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,18 +156,25 @@ await whisperModel.unload();
```

> [!TIP]
> A single `FoundryLocalManager` can manage both chat and audio models simultaneously. See the [chat-and-audio sample](samples/js/chat-and-audio-foundry-local/) for a complete example.
> A single `FoundryLocalManager` can manage both chat and audio models simultaneously.

## 📦 Samples

Explore complete working examples in the [`samples/`](samples/) folder:
Explore complete working examples in the [`samples/`](samples/) folder. These samples
track **`main`** and build the SDK from local source in this repo, so they always reflect
the latest changes:

| Language | Samples | Highlights |
|----------|---------|------------|
| [**C#**](samples/cs/) | 12 | Native chat, audio transcription, tool calling, model management, web server, tutorials |
| [**JavaScript**](samples/js/) | 12 | Native chat, audio, Electron app, Copilot SDK, LangChain, tool calling, tutorials |
| [**Python**](samples/python/) | 9 | Chat completions, audio transcription, LangChain, tool calling, tutorials |
| [**Rust**](samples/rust/) | 8 | Native chat, audio transcription, tool calling, web server, tutorials |
| [**C#**](samples/cs/) | 4 | Chat (native + web server), embeddings, audio (live + file), responses (vision) |
| [**JavaScript**](samples/js/) | 4 | Chat (native + web server), embeddings, audio (live + file), responses (vision) |
| [**Python**](samples/python/) | 4 | Chat (native + web server), embeddings, audio (live + file), responses (vision) |
| [**Rust**](samples/rust/) | 4 | Chat (native + web server), embeddings, audio (live + file), responses (vision) |
| [**C++**](samples/cpp/) | 4 | Chat (native + web server), embeddings, audio (live + file), responses (vision) |

> [!TIP]
> Looking for comprehensive, version-pinned samples (used across Microsoft Learn)? See
> [microsoft-foundry/foundry-samples](https://github.com/microsoft-foundry/foundry-samples/).

## 🖥️ CLI

Expand Down
17 changes: 11 additions & 6 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ Foundry Local is a unified local AI runtime that supports both **text generation

## Samples

- [JavaScript: Native Chat Completions](../samples/js/native-chat-completions/) — Chat completions using the native SDK API
- [JavaScript: Audio Transcription](../samples/js/audio-transcription-example/) — Speech-to-text with Whisper
- [JavaScript: Chat + Audio](../samples/js/chat-and-audio-foundry-local/) — Unified chat and audio in one app
- [JavaScript: Tool Calling](../samples/js/tool-calling-foundry-local/) — Function calling with local models
- [JavaScript: Electron Chat App](../samples/js/electron-chat-application/) — Desktop chat application
- [C#: Getting Started](../samples/cs/GettingStarted/) — C# SDK examples including audio transcription
Working examples for each language live in the [`samples/`](../samples/) folder. Each
language provides the same four samples, built from local SDK source (tracking `main`):

- **chat-completion** — native inference plus the local OpenAI-compatible web server (`/v1/chat/completions`)
- **embeddings** — text embeddings (single and batch)
- **audio** — live microphone transcription (Nemotron) and file-based transcription (Whisper)
- **responses-api** — vision via the local web server Responses API (`/v1/responses`)

See [`samples/README.md`](../samples/README.md) for details. For comprehensive, version-pinned
samples (used across Microsoft Learn), see
[microsoft-foundry/foundry-samples](https://github.com/microsoft-foundry/foundry-samples/).
62 changes: 54 additions & 8 deletions samples/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,61 @@
# Foundry Local Samples

Explore complete working examples that demonstrate how to use Foundry Local — an end-to-end local AI solution that runs entirely on-device. These samples cover chat completions, embeddings, audio transcription, tool calling, LangChain integration, and more.
A small, focused set of working examples for [Foundry Local](https://learn.microsoft.com/azure/foundry-local/) — an end-to-end local AI solution that runs entirely on-device.

> **New to Foundry Local?** Check out the [main README](../README.md) for an overview and quickstart, or visit the [Foundry Local documentation](https://learn.microsoft.com/azure/foundry-local/) on Microsoft Learn.

## These samples track `main`

Every sample here **consumes the SDK from local source in this repository** and therefore
reflects the current state of `main` — they are intentionally **not pinned** to a published
package version. Concretely:

| Language | How the SDK is consumed | Built from |
|----------|-------------------------|------------|
| **C#** | `ProjectReference` to `sdk/cs/src/Microsoft.AI.Foundry.Local.csproj` | `sdk/cs` |
| **JavaScript** | `file:` dependency on the local SDK (`foundry-local-sdk`) | `sdk/js` |
| **Python** | editable install (`-e ../../../sdk/python`) in `requirements.txt` | `sdk/python` |
| **Rust** | `path` dependency (`foundry-local-sdk = { path = "../../../sdk/rust" }`) | `sdk/rust` |
| **C++** | links the locally built `foundry_local_cpp` library | `sdk_v2/cpp` |

> Build the relevant SDK first (see each sample's README), then build/run the sample. Because
> the samples reference local source, there is no version to bump — they always use the code
> currently checked out.

> **What "local source" means precisely:** the Foundry Local **SDK binding** always resolves to
> the in-repo source above — never a published PyPI/npm/crates/NuGet release. Only the
> third-party **native runtime** (ONNX Runtime / GenAI / Foundry Core native) is fetched from
> public package feeds, exactly as each SDK itself obtains it.

> **Hardware acceleration (WinML):** for simplicity and consistency, these samples use the
> standard cross-platform SDK on **all** platforms (Windows, macOS, Linux). Windows hardware
> acceleration via WinML is a capability of the SDK itself, not wired into these samples — see
> the [main README](../README.md) quickstart to enable it in your own app.

## Want version-pinned or comprehensive samples?

For a broader catalog of samples pinned to specific released versions — including the examples
referenced from **Microsoft Learn** content — see:

> 👉 **[microsoft-foundry/foundry-samples](https://github.com/microsoft-foundry/foundry-samples/)**

## What's included

Each language provides the same four samples:

| Sample | Description |
|--------|-------------|
| **chat** (`chat-completion`) | Runs a prompt through **native in-process inference**, then the **same prompt over the local web server** (OpenAI-compatible `/v1/chat/completions`). |
| **embeddings** (`embeddings`) | Generates text embeddings (single and batch) using the native SDK. |
| **audio** (`audio`) | **Live** microphone streaming transcription (Nemotron ASR) by default, plus **file-based** transcription (Whisper) via `--file <path>`. |
| **responses** (`responses-api`) | Vision (image understanding) via the local web server **Responses API** (`/v1/responses`). |

## Samples by Language

| Language | Samples | Description |
|----------|---------|-------------|
| [**C#**](cs/) | 14 | .NET SDK samples including native chat, embeddings, audio transcription, tool calling, model management, web server, vision via Responses API, tutorials, and WinML EP verification. Uses WinML on Windows for hardware acceleration. |
| [**JavaScript**](js/) | 16 | Node.js SDK samples including native chat, embeddings, audio transcription, Electron desktop app, Copilot SDK integration, LangChain, tool calling, web server, vision via Responses API, tutorials, and WinML EP verification. |
| [**Python**](python/) | 14 | Python samples using the OpenAI-compatible API, including chat, embeddings, audio transcription, LangChain integration, tool calling, web server, Responses API, tutorials, and WinML EP verification. |
| [**Rust**](rust/) | 12 | Rust SDK samples including native chat, embeddings, audio transcription, tool calling, web server, vision via Responses API, tutorials, and WinML EP verification. |
| [**C++**](cpp/) | 1 | C++ sample for live audio transcription. |
| Language | Folder | Notes |
|----------|--------|-------|
| **C#** | [`cs/`](cs/) | .NET SDK. |
| **JavaScript** | [`js/`](js/) | Node.js SDK. |
| **Python** | [`python/`](python/) | Python SDK (OpenAI-compatible API for web-server samples). |
| **Rust** | [`rust/`](rust/) | Rust SDK (Cargo workspace). |
| **C++** | [`cpp/`](cpp/) | C++ SDK (`sdk_v2/cpp`); build the SDK with `python sdk_v2/cpp/build.py` first. |
2 changes: 2 additions & 0 deletions samples/cpp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Sample build trees
build/
43 changes: 43 additions & 0 deletions samples/cpp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Foundry Local — C++ Samples

Self-contained C++ samples for the **`sdk_v2/cpp`** SDK (the C++ rewrite, public
header `foundry_local/foundry_local_cpp.h`).

These samples track **`main`**: they build against your **local** `sdk_v2/cpp`
build, not a pinned SDK release.

## Build the SDK first

Every sample links the locally-built SDK shared library, so build it once:

```bash
python ../../sdk_v2/cpp/build.py
```

This produces `sdk_v2/cpp/build/<platform>/<config>/` (default config
`RelWithDebInfo`). The shared `cmake/FoundryLocalSDK.cmake` module locates that
output automatically; override it with `-DFOUNDRY_LOCAL_BUILD_CONFIG=...`,
`-DFOUNDRY_LOCAL_SDK_DIR=...`, or `-DFOUNDRY_LOCAL_BUILD_DIR=...` if needed.

## Samples

| Sample | What it shows |
|-------------------------------------|-------------------------------------------------------------------------------|
| [`chat-completion`](chat-completion)| One chat prompt, run natively in-process **and** over the local web server (`POST /v1/chat/completions`), including streaming. |
| [`embeddings`](embeddings) | Native single and batch text embeddings. |
| [`audio`](audio) | Streaming ASR transcription from live mic (optional PortAudio) or a WAV file. |
| [`responses-api`](responses-api) | Vision / image understanding over the local web server (`POST /v1/responses`). |

## Build and run a sample

Each sample is standalone:

```bash
cd chat-completion # or embeddings, audio, responses-api
cmake -S . -B build
cmake --build build
./build/<target> # see the sample's README for the exact target/args
```

Shared, header-only helpers (`common/`) and the build-wiring module (`cmake/`) are
reused across samples.
50 changes: 50 additions & 0 deletions samples/cpp/audio/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) Microsoft. All rights reserved.
#
# Standalone build for the Foundry Local C++ audio transcription sample.
# Build the SDK first: python ../../../sdk_v2/cpp/build.py
# Then: cmake -S . -B build && cmake --build build
#
# PortAudio is OPTIONAL: when found, live microphone capture is enabled
# (HAS_PORTAUDIO). Without it, the sample still builds and transcribes files /
# synthetic audio.

cmake_minimum_required(VERSION 3.20)
project(foundry_local_audio_sample CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Locate the locally-built SDK and define the foundry_local_cpp target.
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/FoundryLocalSDK.cmake)

add_executable(audio main.cc)

target_link_libraries(audio PRIVATE foundry_local_cpp)

# Absolute path to this sample dir so the bundled Recording.wav is found
# regardless of where the executable runs from.
target_compile_definitions(audio PRIVATE SAMPLE_SOURCE_DIR="${CMAKE_CURRENT_LIST_DIR}")

# --- Optional PortAudio for live microphone capture -------------------------
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(PORTAUDIO QUIET portaudio-2.0)
endif()

find_path(PORTAUDIO_INCLUDE_DIR portaudio.h
HINTS ${PORTAUDIO_INCLUDE_DIRS} /opt/homebrew/include /usr/local/include)
find_library(PORTAUDIO_LIBRARY NAMES portaudio
HINTS ${PORTAUDIO_LIBRARY_DIRS} /opt/homebrew/lib /usr/local/lib)

if(PORTAUDIO_INCLUDE_DIR AND PORTAUDIO_LIBRARY)
message(STATUS "PortAudio found — live microphone capture enabled")
target_compile_definitions(audio PRIVATE HAS_PORTAUDIO)
target_include_directories(audio PRIVATE ${PORTAUDIO_INCLUDE_DIR})
target_link_libraries(audio PRIVATE ${PORTAUDIO_LIBRARY})
else()
message(STATUS "PortAudio not found — building file/synthetic-only (no live mic)")
endif()

# Bake in the rpath so the executable finds the SDK shared library at runtime.
foundry_local_configure_sample(audio)
Loading
Loading