A minimal demo showing how Docker Model Runner (DMR) exposes the same local model through three native API formats (OpenAI, Anthropic, Ollama) on a single endpoint, and how that unlocks dev/prod parity with Microsoft Foundry.
Most local LLM runtimes speak one protocol. DMR speaks three, natively, on the same port. That means:
- One model in memory, consumed by the OpenAI SDK, the Anthropic SDK, or any Ollama-compatible client.
- No translation proxy, no LiteLLM in the middle, zero extra hops.
- Same SDK code in development (against local DMR) and production (against Microsoft Foundry or Anthropic API). One env var changes.
This repo proves it with six short, runnable examples.
- Docker Desktop 4.58.0+ (Anthropic API support requires this version)
- Python 3.10+ (for the Python examples)
- .NET 10 SDK (for the C# examples —
dotnet runfrom theexamples/dotnetproject) - GPU optional, llama.cpp runs on CPU and Apple Silicon
# Enable DMR with TCP access on port 12434
docker desktop enable model-runner --tcp 12434
# Verify it is running
curl http://localhost:12434/engines/v1/modelsPick one. The examples default to ai/qwen3-coder (good for coding, 128K context).
docker model pull ai/qwen3-coder
# Alternatives:
# docker model pull ai/llama3.2 # smaller, faster
# docker model pull ai/devstral-small-2 # coding-focused
# docker model pull ai/mistral # general purposepython -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtcp .env.example .env
# Edit .env if you want to test the production-parity examplepython examples/01_openai_local.pyDMR exposes /engines/v1/chat/completions. The OpenAI Python SDK sees it as a regular OpenAI endpoint. No translation.
python examples/02_anthropic_local.pyDMR exposes /v1/messages. The Anthropic Python SDK sees it as a regular Anthropic endpoint. Same model, same memory, just a different protocol.
python examples/03_streaming.pyToken-by-token streaming works identically on both protocols.
# Development: hit local DMR
ENVIRONMENT=development python examples/04_dev_prod_parity.py
# Production: hit Microsoft Foundry (requires .env values)
ENVIRONMENT=production python examples/04_dev_prod_parity.pySame client code, same SDK calls. Only the base_url and credentials change.
bash examples/05_curl_examples.shHit the three protocols directly with curl. Useful for debugging and for understanding what each SDK sends under the hood.
python examples/06_ollama_local.pyDMR exposes /api/chat. The Ollama Python SDK sees it as a regular Ollama server. Same model, same memory, third protocol. This completes the picture: one model in memory, three Python SDKs, zero proxies.
The same five examples are available as a single .NET 10 console project under examples/dotnet/.
Packages used:
| SDK | NuGet package | Version |
|---|---|---|
| OpenAI | OpenAI |
2.10.0 |
| Azure OpenAI (Foundry) | Azure.AI.OpenAI |
2.1.0 |
| Anthropic (official) | Anthropic |
12.17.0 |
| Ollama | OllamaSharp |
5.4.25 |
Run:
# Build once
dotnet restore examples/dotnet
# Then pick any example
dotnet run --project examples/dotnet -- 01 # OpenAI SDK
dotnet run --project examples/dotnet -- 02 # Anthropic SDK (official)
dotnet run --project examples/dotnet -- 03 # Streaming (both SDKs)
dotnet run --project examples/dotnet -- 04 # Dev/prod parity
ENVIRONMENT=production dotnet run --project examples/dotnet -- 04
dotnet run --project examples/dotnet -- 06 # Ollama SDKThe project reads the same .env file at the repo root via DotNetEnv.Env.TraversePath().Load(), so no extra configuration is needed.
| Pattern | Demonstrated by |
|---|---|
| One model, three SDKs, no proxy | 01, 02, 06 |
| Streaming parity across protocols | 03 |
| Dev/prod parity with Foundry | 04 |
| Raw protocol inspection | 05 |
┌─────────────────────────────────────────────────────────┐
│ Your laptop │
│ │
│ ┌──────────────┐ ┌────────────────────────────┐ │
│ │ OpenAI SDK │─────▶│ /engines/v1/chat/completions│ │
│ └──────────────┘ │ │ │
│ │ Docker Model Runner │ │
│ ┌──────────────┐ │ │ │
│ │ Anthropic SDK│─────▶│ /v1/messages │ │
│ └──────────────┘ │ │ │
│ │ ┌──────────────────────┐ │ │
│ ┌──────────────┐ │ │ ai/qwen3-coder │ │ │
│ │ Claude Code │─────▶│ │ (one model, in memory)│ │ │
│ └──────────────┘ │ └──────────────────────┘ │ │
│ └────────────────────────────┘ │
│ localhost:12434 │
└─────────────────────────────────────────────────────────┘
│
│ same SDK calls, swap base_url
▼
┌──────────────────────┐
│ Microsoft Foundry │
│ (production) │
└──────────────────────┘
| Protocol | Path | Used by |
|---|---|---|
| OpenAI Chat Completions | /engines/v1/chat/completions |
openai, langchain-openai, AI SDK |
| OpenAI Responses | /engines/v1/responses |
openai (reasoning models) |
| Anthropic Messages | /v1/messages |
anthropic, Claude Code |
| Anthropic Token Count | /v1/messages/count_tokens |
anthropic |
| Ollama Chat | /api/chat |
Ollama clients |
MIT
Pablo Piovano — Director of AI at OZ Digital · Microsoft MVP in AI
Built as a companion to a LinkedIn article on #MicrosoftFoundry and local AI development.