Appearance
SPIKE-31: Cross-track capability and feature parity across the three deployment targets
Role: foundry-researcher (Opus). Status: research spike complete. Read-only: no Azure resources created, read, or modified; no az commands run; no call made against any endpoint, local or hosted; no software installed. First-party documentation review only. Date: 2026-07-30 Scope: capability and feature parity across the three deployment targets this repository's methodology covers. Track 1 is Azure AI Foundry (AIServices) in an Azure region. Track 2 is Foundry Local on a Windows Server host. Track 3 is Foundry Local on Azure Local. This spike exists to fill the cells currently marked UNKNOWN (SPIKE-31) in docs/targets/index.md, and its primary deliverable is a set of three-column tables an editor can lift into that page verbatim. Every factual claim is grounded in a first-party (Microsoft) source, cited inline. Anything Microsoft has not published is marked UNKNOWN with the test or document that would resolve it. This spike authorizes no deployment and no spend.
Depends on: SPIKE-08 (the on-device assessment), SPIKE-09 (the original Azure Local assessment), SPIKE-18 and ADR-0013 (track 2's install mechanism and honest governance scope), SPIKE-19 and ADR-0014 (track 3's layer model, GPU scope, and authentication), ADR-0011 (the three-track numbering used throughout), ADR-0012 (the APIM AI gateway posture for agent MCP tools), and the track 1 feature surface as documented in docs/guide/using-your-deployment.md and docs/guide/building-agents.md. This spike verifies and extends those records against current Microsoft documentation; it does not restate them.
Headline: the three tracks share an API shape and almost nothing else. All three speak an OpenAI-compatible /v1/chat/completions, and that similarity is the trap. Beneath it, track 1 has a platform (agents, retrieval, fine-tuning, batch, quotas, and a mandatory content filter) and the two local tracks have a model server. The single most governance-relevant finding is that neither local track documents any content filtering or responsible-AI guardrail at all, so an adopter moving a workload from track 1 to track 2 or track 3 silently loses every safety control ADR-0007 relies on. The second is that track 2's endpoint has no authentication whatsoever, while track 3's has two modes and both are mandatory, which makes the two local tracks far less alike than "Foundry Local" as a shared brand name suggests.
Question
Thirteen questions, each answered for all three tracks:
- API surface. What endpoints does each expose, is each OpenAI-compatible, and to what version of that contract? What differs on paths, the
modelfield,api-version, streaming, function and tool calling, structured outputs, and JSON mode? - SDK support. Which official SDKs work against each, and what is the client configuration difference?
- Authentication modes, restated crisply per track: what authenticates a caller, and what does not exist.
- Agents. Is Foundry Agent Service available on each? Can an agent framework target each? What about MCP tool calling, and how does ADR-0012's APIM gateway posture apply per track?
- Retrieval and RAG. What is built in, and what is bring-your-own?
- Fine-tuning, distillation, and model customization.
- Batch inference and asynchronous processing.
- Content safety and responsible AI.
- Quotas, rate limits, and concurrency.
- Multi-tenancy and multi-user serving.
- Disconnected and air-gapped operation.
- Management and UI surface.
- Structured outputs, tool calling, and vision input at the API level, independent of any given model.
Findings
Q1. API surface: three OpenAI-compatible servers, three different contracts
All three expose POST /v1/chat/completions and all three follow OpenAI Chat Completions conventions. That is where the parity ends.
Track 1. The v1 API is the current GA surface. Base URL is https://<resource>.openai.azure.com/openai/v1/ or https://<resource>.services.ai.azure.com/openai/v1/, both accepted. Microsoft states the two key departures from the older Azure-specific shape directly: "OpenAI() client is used instead of AzureOpenAI()," "Pass the Azure OpenAI endpoint to base_url and append /openai/v1 to the endpoint address," and "api-version is no longer a required parameter with the v1 GA API." Preview features are opted into with feature-specific headers or by an alpha segment in the path rather than by swapping API versions. Source: Azure OpenAI in Microsoft Foundry Models v1 API. The surface is broad: /openai/v1/responses (the recommended entry point for Azure OpenAI models), /openai/v1/chat/completions, /openai/v1/embeddings, /openai/v1/images/generations, /openai/v1/evals, /openai/v1/fine_tuning/..., and the Batch endpoints. Sources: same page, plus Quotas and limits for the /embeddings, /chat/completions, and Batch limits.
One track 1 wrinkle this repository has already proven the hard way and which belongs in any parity table: the two hostnames are interchangeable for chat but not for images. Image generation is served on the openai.azure.com hostname and wants an explicit api-version=preview; the same path on services.ai.azure.com returns {"error":{"code":"not_found","message":"Requested path is not found"}}. Source: this repository's own executed samples in docs/guide/using-your-deployment.md, which records that this was found by running the call, not by reading the docs.
Track 2. Foundry Local's optional local server exposes a small, explicitly volatile surface. Microsoft's own caution on the reference page: "This API is under active development and may include breaking changes without notice." The documented endpoints are:
| Endpoint | Method | Purpose |
|---|---|---|
/v1/chat/completions | POST | Chat completions, "fully compatible with the OpenAI Chat Completions API" |
/v1/audio/transcriptions | POST | Whisper transcription, OpenAI audio-transcription compatible |
/v1/chat/completions/tokenizer/encode/count | POST | Token count without inference |
/openai/status | GET | Server binding, model directory, pipe name |
/openai/models | GET | Cached models |
/openai/loadedmodels | GET | Currently loaded models |
/foundry/list | GET | Catalog listing, including a per-model supportsToolCalling flag |
/openai/download | POST | Pull a model into the cache |
/openai/load/{name}, /openai/unload/{name}, /openai/unloadall | GET | Model memory lifecycle, with ttl and ep query parameters |
/openai/getgpudevice, /openai/setgpudevice/{deviceId} | GET | GPU device selection |
Source: Foundry Local REST API Reference.
Three things about that surface matter for parity. First, there is no /v1/embeddings endpoint and no image or speech-synthesis endpoint, which confirms SPIKE-08's finding against the current reference. Second, the request body documents stream, functions, function_call, and tools ("Tools calculated for the request"), plus Foundry-Local-specific extensions ep (override the ONNX execution provider), ttl (model time to live in memory), top_k, and random_seed. Third, response_format is documented on /v1/audio/transcriptions but not on /v1/chat/completions, and the documented messages[].content type is a plain string. Same source. Track 2's chat contract is therefore closer to the 2023-era OpenAI Chat Completions shape (with the legacy functions and function_call fields still first-class) than to the current one.
Separately, the product overview claims a broader compatibility than the REST reference documents: "Supports OpenAI request and response formats including the OpenAI Responses API format." Source: What is Foundry Local?. No Responses endpoint appears in the REST reference. That discrepancy is carried as UNKNOWN #1.
The port is dynamic and Microsoft is emphatic: "never hardcode the port," discover it via manager.endpoint (JS) or config.Web.Urls (C#), or from GET /openai/status. Source: REST API Reference.
Track 3. Track 3 has two API planes, which neither the hub nor ADR-0014 currently records, and this is a genuine addition.
The data plane is per model deployment, on the deployment's own address (gateway host plus the model's metadata.name, or the in-cluster service name on port 5000):
| Endpoint | Method | Purpose |
|---|---|---|
/health | GET | Liveness |
/ready | GET | Readiness, 200 OK when the model is loaded |
/v1/model | GET | Loaded-model metadata |
/v1/models | GET | "OpenAI-compatible model listing" (chat server, port 5000) |
/v1/chat/completions | POST | Generative inference. "When using models with tool calling capabilities, include the tool_choice field in the request payload." |
/v1/audio/transcriptions | POST | Transcription |
/v1/predict | POST | Predictive (ONNX classification, regression) inference, on the predictive server, port 8000 |
Sources: Inference API endpoints and payload reference, Foundry inference API reference.
The control plane is a separate FastAPI service on port 8080 with base path /api/v1, offering full CRUD over models, deployments, and API keys: GET/POST /api/v1/models, POST /api/v1/models/sync, GET/POST/PUT/PATCH/DELETE /api/v1/namespaces/{ns}/deployments[/{name}], and GET /api/v1/namespaces/{ns}/deployments/{name}/keys plus POST .../keys/{key_type}/rotate. Microsoft states: "All APIs use REST/HTTP. The platform doesn't include any gRPC endpoints. All services enforce authentication via Azure role-based access control (Azure RBAC) or API keys." Source: Foundry inference API reference.
No /v1/embeddings is documented on track 3 either. response_format and structured-output parameters are not documented on track 3's chat completions. The documented request examples use model, messages, temperature, max_tokens. Same sources.
The model field differs on all three, and this is the single most common integration error. On track 1 the value is your deployment name, not the vendor model id (404 DeploymentNotFound is what you get for confusing them; see docs/guide/using-your-deployment.md). On track 2 it is the local model id as loaded, for example qwen2.5-0.5b-instruct-generic-cpu or Phi-4-mini-instruct-generic-cpu. On track 3 it is the catalog model id including its version suffix, for example Phi-4-generic-cpu:1 or Phi-3.5-mini-instruct-cuda-gpu:1, while the URL path segment carries the ModelDeployment resource name, for example /phi-4-cpu/v1/chat/completions. Sources: Run inference on Foundry Local on Azure Local, Foundry Local REST API Reference.
Q2. SDK support: the stock OpenAI SDK works against all three, and that is the strongest parity result in this spike
Track 1. Microsoft documents the plain OpenAI() client (not AzureOpenAI()) for Python, C#, JavaScript, Go, and Java against the v1 base URL, with either an API key or a token provider passed as api_key. The v1 API "removes this dependency, by adding automatic token refresh support to the OpenAI() client." Source: v1 API. The Azure-native SDKs (Azure.AI.OpenAI, azure-ai-projects) also work, and azure-ai-projects is what Agent Service needs.
Track 2. Microsoft documents Python, C#, JavaScript, and Rust, in two package flavours: cross-platform (foundry-local-sdk, Microsoft.AI.Foundry.Local) and Windows (foundry-local-sdk-winml, Microsoft.AI.Foundry.Local.WinML, which "integrates with the Windows ML runtime"). Every documented sample pairs the Foundry Local SDK with the stock openai package: the SDK downloads, loads, and starts the web service, then openai.OpenAI(base_url=..., api_key="none") talks to it. The literal placeholder key values in Microsoft's own samples are "none" (Python), "notneeded" (JavaScript and C#). Source: Integrate with inference SDKs. LangChain is documented as a supported integration path. Source: What is Foundry Local?, which names the optional local server as being for "integrating with tools like LangChain."
The material client-configuration difference for track 2 is not the SDK, it is the lifecycle: the endpoint does not exist until something starts it. Track 1 and track 3 endpoints are always-on services; a track 2 client must start the service, ensure the model is downloaded and loaded, discover the dynamic port, and then call. Microsoft's samples do exactly that, and end with model.unload() and stop_web_service(). Same source.
Track 3. Microsoft's own examples for track 3 are curl, PowerShell Invoke-RestMethod, and kubectl run curl-run. No language SDK is documented for track 3 in the pages reviewed. Source: Run inference. The absence is not a statement that SDKs fail: the endpoint is OpenAI-compatible on /v1/chat/completions, accepts the key in either Authorization: Bearer or api-key headers (the latter described as the "OpenAI-compatible" form), and Agentic Retrieval's own bring-your-own-model path accepts "an external language model endpoint that supports the OpenAI-compatible chat completions API," which is exactly what a stock OpenAI client speaks. Sources: Inference API endpoints and payload reference, Agentic Retrieval overview. But it is not a documented, first-party-supported configuration, so the parity table says so rather than asserting it works. Carried as UNKNOWN #2.
Q3. Authentication, restated crisply per track
This is the clearest three-way divergence in the whole spike, and the hub's current wording understates it.
Track 1: two modes, both real. Microsoft Entra ID with a bearer token (the recommended default; the prerequisite is "the Cognitive Services OpenAI User role assigned to your identity"), or an API key. Azure accepts the key in either the Azure-native api-key header or the OpenAI-shaped Authorization: Bearer header, which is precisely why generic OpenAI clients work. Sources: v1 API, and this repository's executed samples in docs/guide/using-your-deployment.md. Azure RBAC on the endpoint is real, and a valid token from a principal without a role gets 401 or 403.
Track 2: none. The CLI reference states "Azure RBAC: Not applicable (runs locally)" and no Azure subscription is required at all. Sources: Foundry Local CLI reference, What is Foundry Local?. No authentication mechanism appears anywhere in the REST API reference: no auth header is documented on any endpoint, and Microsoft's own SDK samples pass the literal strings "none" and "notneeded" as the API key because the value is ignored. Sources: REST API Reference, Integrate with inference SDKs. What does not exist on track 2: Entra ID, Azure RBAC, API keys, Key Vault integration, token expiry, and any per-caller identity at all. The only access control is the host's own network and process boundary. ADR-0013 decision 10 already says this; this spike confirms it against the current reference and adds that the placeholder-key convention in Microsoft's samples is the practical proof.
Track 3: two modes, and authentication is not optional. "All endpoints require authentication. The platform supports two methods: API key authentication and Microsoft Entra ID JSON Web Token (JWT) authentication." Accepted header formats are Authorization: Bearer <api-key>, api-key: <api-key>, and Authorization: Bearer <jwt-token>. Invalid keys are rejected with 401. Source: Inference API endpoints and payload reference.
Two track 3 details sharpen the governance story and one of them is a caution ADR-0014 does not currently record:
- Entra ID authorization resolves to a named Azure role. "Entra ID authentication requires the Cognitive Services OpenAI User role (or equivalent) assigned to the caller identity on the cluster scope." This is the same role name track 1 uses, which is a genuine and useful parity point. Source: Run inference.
- "API key authentication grants full access without role checks." Same source, quoted verbatim. That is a plain statement that track 3's API key path bypasses RBAC entirely, which strengthens ADR-0014 decision 5's choice of Entra from something aesthetic to something load-bearing. Note also that the keys are generated by the operator with a recognisable
fndry-pk-/fndry-sk-prefix and are retrievable and rotatable over the control-plane API, so anyone withdeployments/readon the control plane can read them. Source: Foundry inference API reference. - The control plane has its own RBAC split: "GET and HEAD requests require the
deployments/readaction, POST, PUT, and PATCH requests requiredeployments/write, and DELETE requests requiredeployments/delete." Same source.
Q4. Agents, agent frameworks, MCP, and where ADR-0012 applies
Track 1: full Foundry Agent Service. "Foundry Agent Service is a managed platform for building, deploying, and scaling AI agents. Use any framework, any supported model from the Foundry model catalog, and the Responses API as a single entry point." Two agent shapes: prompt agents (configuration only, Foundry runs them) and hosted agents (your container, Foundry runs it with "a managed endpoint, automatic scaling, a dedicated Microsoft Entra identity, session-level state persistence, and end-to-end observability"). Documented hosted-agent frameworks: Agent Framework, LangGraph, the OpenAI Agents SDK, the Anthropic Agent SDK, the GitHub Copilot SDK, or your own code. A third path exists with no agent resource at all: "Call the Responses API from your existing process to get Foundry models and platform tools without moving your code." Source: What is Microsoft Foundry Agent Service?. Agent Service needs a project, not just an account; the project endpoint has its own shape, https://<account>.services.ai.azure.com/api/projects/<project-name>. Source: docs/guide/building-agents.md.
MCP on track 1 is first-class: "Foundry supports remote MCP servers that you can add from the Add Tools catalog," with key-based, Entra (agent or project managed identity), OAuth on-behalf-of, or unauthenticated tool authentication, plus Toolbox, which exposes a curated tool set "through a single MCP-compatible endpoint" consumable by "any MCP-compatible agent runtime or client." Source: Agent Service overview.
Track 2: no agent service, but any framework can point at it. Nothing resembling Foundry Agent Service exists for the device runtime, and no MCP server or MCP tool surface appears anywhere in the Foundry Local documentation set reviewed. What track 2 has is an OpenAI-compatible base URL, which is all a bring-your-own-runtime framework needs; Microsoft names LangChain explicitly as a target for the optional local server. Sources: What is Foundry Local?, Integrate with inference SDKs. Tool calling at the API level is present (tools, functions, function_call in the request body, and a per-model supportsToolCalling flag in the catalog listing), so an agent loop that does its own tool orchestration is viable against a tool-capable model. Source: REST API Reference.
Track 3: Foundry Agent Service is NOT available, but a different first-party agent platform is, and it is the correction this spike most wants to make to the hub. The hub currently reads UNKNOWN (SPIKE-31) for "Foundry Agent Service" on track 3. The honest answer is two-part:
- Foundry Agent Service itself is a Microsoft Foundry resource feature and does not appear in the Foundry Local on Azure Local documentation set. Track 3's own supported-workload list is "Generative AI inference," "Predictive AI inference," "CPU and GPU execution," and "Multi-model serving patterns," with no agent runtime. Source: What is Foundry Local on Azure Local?.
- A separate Arc extension, Agentic Retrieval in Foundry Local, supplies an on-premises agent platform on the same cluster. It "combines a knowledge layer (document ingestion, embedding, vector search) with an agentic layer (AI agents, knowledge orchestration, Model Context Protocol (MCP) server)." Its agentic layer provides "Agent execution," "Knowledge orchestration," "MCP server - a built-in MCP server with search tools, plus support for connecting to external MCP servers," and "Conversation management - threads, messages, and runs." Its chat UI "communicates with the agents runtime through the Foundry Agents API," and runs "support streaming via Server-Sent Events." Source: Agentic Retrieval and Agents and Tools with Foundry Local Overview.
So the correct cell for track 3 is not "no agents." It is: no Foundry Agent Service, but a first-party on-premises agent runtime with a built-in MCP server and external-MCP support exists as a separate, additionally installed, separately preview-gated Arc extension. It is also deployable in three modes, combined, agentic, or knowledge, and in agentic mode "agents connect to external MCP servers instead" of local collections. Same source.
ADR-0012's APIM gateway posture, per track. ADR-0012 adopts the APIM AI gateway as the governance control for Foundry Agent MCP tools, gated to a future agent phase, and records that "governance is enabled at the Foundry-resource level and requires an APIM instance connected to the resource." Source: docs/adr/ADR-0012-agent-mcp-gateway-governance.md, citing Configure AI Gateway in your Foundry resources and Govern MCP tools by using an AI gateway (preview). Applying that:
- Track 1: applies, exactly as ADR-0012 states. Gated to the agent phase, not provisioned now.
- Track 2: does not apply, and there is nothing for it to govern. There is no Foundry resource to enable it on, no Foundry Agent, and no MCP tool surface. This is an inference from the feature's documented prerequisite (resource-level enablement on a Foundry resource) plus the absence of any Foundry resource or agent surface on track 2, not a Microsoft statement about track 2; it is labelled as such.
- Track 3: does not apply to the Foundry Local extension, and is not the control for Agentic Retrieval's MCP server either. Track 3's inference endpoint is a Kubernetes service behind a Gateway API, not a
Microsoft.CognitiveServicesaccount, so resource-level gateway enablement has no target. Agentic Retrieval's MCP server is governed by its own Entra integration and Azure RBAC, including theEdgeRAGDeveloperEntra role for registering MCP servers as knowledge sources. Source: Agentic Retrieval overview. This is a real governance gap worth naming: ADR-0012 selects one control for one track, and track 3's agent MCP traffic, if that extension is ever installed, needs a separate decision. Nothing in this spike closes it.
Q5. Retrieval and RAG
Track 1: bring your own, plus a rich set of platform options. Agent Service ships built-in tools "including web search, file search, memory, code interpreter, MCP servers, and custom functions," and supports bring-your-own resources including Azure AI Search and Azure Cosmos DB for conversation state. Source: Agent Service overview. Track 1 also carries a Groundedness content filter that "flags whether the text responses of large language models are grounded in the source materials provided by the users," available for streaming scenarios only and in four named regions. Source: Content filtering for Microsoft Foundry Models.
Track 2: bring your own, with no retrieval primitive at all. There is no embeddings endpoint, no vector store, and no retrieval component in the Foundry Local documentation set. Anything RAG-shaped is entirely the caller's problem, and the caller must supply its own embedding model from somewhere else, because track 2 cannot generate embeddings. Source: REST API Reference (absence of an embeddings endpoint), What is Foundry Local? (catalog covers "chat completions ... and audio transcription").
Track 3: Agentic Retrieval, and its requirements are specific. The knowledge layer provides "data ingestion - parse, chunk, and embed documents from on-premises file shares," "collections" mapping to "Milvus vector collections and Postgres tables," "multiple search types - choose from hybrid, vector, text, and hybrid multimodal," and "per-collection Azure role-based access control." Source: Agentic Retrieval overview.
The GPU and model requirements the tasking asks about specifically:
- The recommended language model needs its own GPU. "The recommended model is GPT-OSS-20B via Foundry Local on Azure Local which requires its own GPU." Source: What you need for Agentic Retrieval in Foundry Local.
- The embedding models need two more GPUs. "Two GPU-accelerated models for text embedding (BGE-M3) and image embedding (CLIP ViT-L/14) running locally on two GPUs. Docling (document parser) runs on CPU." Source: Agentic Retrieval overview. SPIKE-09 recorded the same shape from the cluster-preparation page: combined mode needs 2 GPU VMs plus 3 or more CPU VMs, and "agentic" mode needs no GPU node pool.
- Entra ID authentication must have been enabled on the Foundry Local extension at install time, or Agentic Retrieval cannot connect to the deployed models. That is ADR-0014 decision 5's forward-compatibility gate, sourced in SPIKE-19 from Requirements for Foundry Local on Azure Local.
- One correction worth carrying: Agentic Retrieval is not a vision model. "Image retrieval - Ingest and retrieve relevant images as contextual references alongside text. Agentic Retrieval in Foundry Local isn't a visual language model (VLM)." Source: Agentic Retrieval overview. This does not close SPIKE-09 UNKNOWN #1 (a vision-capable catalog model for image grading); it narrows it, because image retrieval is not image understanding.
So the honest summary is: track 3's RAG story is real, first-party, and considerably richer than "bring your own," but it is a second preview extension with a three-GPU-shaped default configuration, not a feature of the Foundry Local extension itself.
Q6. Fine-tuning, distillation, and model customization
Track 1: yes, as a hosted service. Three documented methods: supervised fine-tuning, direct preference optimization, and reinforcement fine-tuning, with per-model support. Source: Fine-tuning. The platform limits confirm it is a real service surface rather than a passthrough: "Maximum fine-tuned model deployments: 10," "Total number of training jobs per resource: 100," "Maximum training job time (job fails if exceeded): 720 hours," "Maximum training job size (tokens in training file) x (# of epochs): 2 billion," and a 1 GB total file allowance per resource. Source: Quotas and limits. On distillation specifically: the API changelog records "Stored completions (distillation API support)" added in 2025-02-01-preview, which is the capture mechanism a distillation workflow builds on. Source: v1 API changelog. A distinct, named distillation product surface is not documented on the fine-tuning page, so this spike records stored completions as present and treats "distillation" as a workflow built on it rather than as a separate feature.
Track 2: no fine-tuning service, but genuine model customization. Nothing in the Foundry Local documentation trains a model. What exists is a conversion and optimization path: "Foundry Local runs ONNX models on your device. Use Olive to convert and optimize models from Hugging Face (Safetensors or PyTorch) into ONNX so you can run them with Foundry Local," with olive optimize taking --device, --provider, and --precision (fp16, fp32, int4, int8), then an inference_model.json file to expose the result to the runtime. Source: Compile Hugging Face models and run on Foundry Local. So a model fine-tuned somewhere else can be brought to track 2; the fine-tuning itself happens elsewhere. Note also that Microsoft's own best-practice guidance mentions encrypting disks that cache sensitive fine-tuning data, which implies the bring-a-fine-tuned-model pattern is anticipated. Source: Best practices and troubleshooting guide.
Track 3: no fine-tuning service, but a bring-your-own-model path from an OCI registry. The ModelDeployment custom resource accepts spec.model.custom with registry, repository, tag, and a credentials.secretRef, and the control plane exposes POST /api/v1/models to register a custom model (with SSRF validation on the registry field that "rejects private, internal, and bare IP addresses with a 400 error"). Sources: Run inference, Foundry inference API reference. No training, no fine-tuning job, no distillation endpoint appears anywhere in the track 3 documentation set.
Q7. Batch inference and asynchronous processing
Track 1: yes, a real Batch API with its own quota currency. Global Batch and Data Zone Batch are documented, with limits including "Maximum Batch input files - (no expiration): 500," "Maximum input file size: 200 MB" (1 GB with bring-your-own storage), and "Maximum requests per file: 100,000." Batch quota is measured differently from interactive quota: "Quota values for global batch are represented in terms of enqueued tokens. When you submit a file for batch processing, the number of tokens in the file is counted. Until the batch job reaches a terminal state, those tokens count against your total enqueued token limit." Sources: Quotas and limits, How to use global batch processing. Separately, the Responses API adds "Support for asynchronous background tasks." Source: v1 API changelog.
Track 2: no. No batch endpoint exists in the REST reference, and Microsoft's own comparison against server runtimes says plainly that the capability class is absent: server runtimes "handle concurrent request queuing, continuous batching, and efficient GPU sharing across many simultaneous clients. Foundry Local doesn't provide these capabilities." Sources: REST API Reference, What is Foundry Local?.
Track 3: no batch job API. No batch endpoint appears on either the data plane or the control plane. vLLM is available as the "high-throughput" runtime for GPU deployments, which is a serving-tier throughput property rather than an asynchronous batch job service. Sources: Inference API endpoints and payload reference, Foundry inference API reference, What is Foundry Local on Azure Local?. One asynchronous primitive does exist and is worth recording because it is the only one: the predictive server queues requests and returns a queue-capacity payload with queue_depth and retry_after plus a Retry-After header when full. Source: Foundry inference API reference.
Q8. Content safety and responsible AI: the governance-relevant gap
This is the section an adopter must read before moving a workload between tracks, and the tasking is right to ask for it plainly.
Track 1: a mandatory, layered, configurable content filtering system, on by default. "Microsoft Foundry includes a content filtering system that works alongside core models and image generation models and is powered by Azure AI Content Safety. This system runs both the prompt and completion through an ensemble of classification models designed to detect and prevent the output of harmful content." Four categories (hate and fairness, sexual, violence, self-harm) across four severity levels (safe, low, medium, high), plus optional filters for groundedness, protected material for text and code, PII, user prompt attacks, indirect attacks, and task adherence. Behaviour is explicit: a filtered prompt returns HTTP 400 with "code": "content_filter"; a filtered completion returns 200 with finish_reason set to content_filter. Source: Content filtering for Microsoft Foundry Models.
Three points that matter for a children's-content posture specifically, which is what ADR-0007 governs:
- The sexual category explicitly enumerates "Child exploitation, child abuse, child grooming" as in-scope content the filter detects and blocks. Same source. This is the single strongest first-party justification for keeping any sensitive-audience generative work on track 1.
- Turning filtering off is gated, not a setting. "No filters" and "Annotate only" are available only "if approved," and "only customers who have been approved for modified content filtering have full content filtering control and can turn off content filters." Same source. The default is protective and staying on the default requires no action.
- The filter does not cover audio models. "The content filtering system doesn't apply to prompts and completions processed by audio models such as Whisper." Same source. Anyone assuming blanket coverage across every deployment on a track 1 account is wrong for that one class.
- Filtering can also fail open, and the caller is expected to notice: "If the content filtering system is down or otherwise unable to complete the operation in time, your request still completes without content filtering. You can determine that the filtering wasn't applied by looking for an error message in the
content_filter_resultsobject." Same source. Microsoft's own best practice is "Verify content filter execution." Any pipeline that depends on the filter for a sensitive audience should check this object rather than assume.
Track 2: no content filter is documented. State this plainly, because it is the difference that matters. Across the Foundry Local overview, REST API reference, CLI reference, SDK integration guide, best-practices and troubleshooting guide, and the model compilation guide, no content filter, guardrail, safety classifier, blocklist, or responsible-AI control of any kind appears. Microsoft describes what the runtime does do with prompts: "Foundry Local runs inference entirely on the device. When your application sends prompts to a Foundry Local endpoint, prompts and model outputs are processed locally," with the network used only for model and component downloads and optional diagnostics. Source: What is Foundry Local?. What safety exists is whatever the chosen open-weight model was trained with, plus the model's own licence terms, which Microsoft directs you to read per model with foundry model info <model> --license. Source: Best practices and troubleshooting guide.
This is negative evidence, so state its limits honestly: an absence across six first-party pages is strong but is not a Microsoft statement that no filter exists. It is, however, exactly the kind of absence a governed adopter must treat as "assume none." Carried as UNKNOWN #3.
Track 3: no content filter is documented either. The Foundry Local on Azure Local key-capabilities list covers Kubernetes-native operations, custom resources, OpenAI-compatible REST, CPU and GPU deployments, multi-node scale, disconnected operation, endpoint security via "API keys, Microsoft Entra ID authentication, and TLS-enabled gateway API patterns," and catalog sync. Content filtering is not among them, and no content-filter, guardrail, or safety-classifier component appears in the architecture summary, the inference operator description, the API references, or the requirements page. Sources: What is Foundry Local on Azure Local?, Foundry inference API reference, Inference API endpoints and payload reference.
Track 3 does have real access governance (Entra, Azure RBAC, TLS, per-collection RBAC in Agentic Retrieval), and that is not the same thing as content safety. ADR-0012 already draws this distinction for the APIM gateway ("the gateway governs tool ACCESS, not generated-content safety"); the same distinction applies to everything track 3 offers. Carried as UNKNOWN #3.
The parity statement, said once and plainly: track 1 filters prompts and completions by default and blocks child-exploitation content by category; tracks 2 and 3 document no content filtering of any kind, so on either local track the only safety control is the model's own training and whatever the calling application implements. Any workload governed by ADR-0007 that moves to a local track needs a compensating control decided before it moves, and this spike does not propose one.
Q9. Quotas, rate limits, and concurrency
Track 1: a formal, tiered, subscription-scoped quota system. "Quotas and limits aren't enforced at the tenant level. Instead, the highest level of quota restrictions is scoped at the Azure subscription level," and since May 2026 quota is tracked "at the subscription level rather than per resource or per region," with Global Standard deployments of the same model and version sharing one pool across all regions. Seven tiers exist (Free plus 1 through 6), quotas increase automatically with usage, and a subscription can opt out of auto-upgrade with tierUpgradePolicy: NoAutoUpgrade. Limits are published per model per deployment type in both requests per minute and tokens per minute; image models are RPM-only (for example gpt-image-1 at 9 RPM at Tier 1). Source: Quotas and limits.
Structural limits worth carrying into a parity table because they bound design, not just throughput: "Maximum standard deployments per resource: 32," "Azure OpenAI resources per Azure subscription: 30," "Maximum number of /chat/completions tools: 128," "Maximum number of /chat/completions messages: 2,048," and "GPT-4o and GPT-4.1 maximum images per request: 50." Same source. And the honest caveat Microsoft itself publishes: "You might receive 429 (Too Many Requests) responses even when token usage metrics appear below your quota." Same source. This repository's own guide records the practical corollary: an entry that sets no capacity inherits a fallback of 1, which measures at roughly one request per minute. Corrected 2026-08-02: an earlier revision of this line called that an accidental circuit breaker and attributed the claim to docs/guide/using-your-deployment.md, which never said it. Capacity 1 is not a cost control. GlobalStandard bills per token, so a throttled deployment can spend the same and deliver less, and an agentic caller meets 429 on its second call. Set capacity deliberately per model. Source: docs/guide/deployment.md.
Track 2: no quota system exists. Throughput is bounded by the host and by the runtime's design. No rate limit, no token budget, no throttle, and no 429 semantics appear in the REST reference. What bounds throughput is CPU or GPU capacity, the execution provider chosen, and the model's size, plus the model lifecycle: a model must be loaded into memory to serve, and ttl governs how long it stays there. Microsoft's own remedy for slow inference is hardware, not tuning: issue "Slow inference," cause "CPU-only model with a large parameter count," solution "Use GPU-optimized model variants when available." Sources: REST API Reference, Best practices and troubleshooting guide. No first-party tokens-per-second figure is published for the device SDK, which is SPIKE-18 UNKNOWN #2 and is not closed here.
Track 3: no per-caller quota, but real cluster-level bounds that a deployer sets. Throughput is bounded by spec.replicas (1 to 100, default 1), by spec.resources requests and limits for CPU, memory, and GPU (resources.limits.gpu 1 to 8, required when compute: gpu and skipGpuResource is false), by node capacity, and by runtime choice (vLLM being the GPU-only high-throughput engine). The predictive server has an explicit queue with a documented overflow response carrying queue_depth and retry_after. Sources: Foundry inference API reference, ModelDeployment and operator configuration reference, Inference runtimes. Whether the chat server applies any request limit or returns 429 is not documented; carried as UNKNOWN #4.
The governance consequence, which the hub already gets right and this spike confirms: on track 1 a quota is a cap you can move; on tracks 2 and 3 there is no cap to move, only hardware.
Q10. Multi-tenancy and multi-user serving
Track 1: designed for it. Global Standard "dynamically route[s] customer traffic to the data center with the best availability," quota is a shared subscription pool, and Agent Service hosted agents autoscale "container instances per session and request volume." Sources: Quotas and limits, Agent Service overview.
Track 2: explicitly not designed for it, in Microsoft's own words. The FAQ "Can Foundry Local run on a server?" answers: "Foundry Local is optimized for hardware-constrained devices where a single user accesses the model at a time. While you can technically install and run it on server hardware, it isn't designed as a server inference stack." And: "Server-oriented runtimes like vLLM or Triton Inference Server are built for multi-user scenarios ... Foundry Local doesn't provide these capabilities ... If you need to serve models to multiple concurrent users, use a dedicated server inference framework." Source: What is Foundry Local?. Combined with Q3 (no authentication at all), track 2 has neither the concurrency design nor the per-caller identity that multi-tenancy requires. This is the strongest available support for ADR-0013 decision 10's positioning of track 2 as a single-host capability rather than a governance-equivalent sibling.
Track 3: designed for it, and says so. "Scale inference across multi-node Kubernetes clusters for concurrent usage and high-parameter model support," and "Use ... when you need to ... Scale AI inference across multiple nodes in a Kubernetes cluster for concurrent access and larger models." Source: What is Foundry Local on Azure Local?. Multi-replica vLLM deployments get Endpoint Picker routing via the Gateway API Inference Extension, and Agentic Retrieval scopes threads per user and applies per-collection Azure RBAC. Sources: SPIKE-19's citation of Deploy Foundry Local as an Azure Arc extension, Agentic Retrieval overview.
Q11. Disconnected and air-gapped operation
Track 1: no. It is a hosted service in an Azure region and every call crosses the network to it. No first-party disconnected mode exists.
Track 2: yes, once the cache is populated. "User data never leaves the device, responses start immediately with zero network latency, and your app works offline." The network is used for "model and component downloads" on first use and "optional diagnostics." Source: What is Foundry Local?. This is the most complete air-gap story of the three in the sense that it needs no Azure resource at all, and the weakest in the sense that it comes with no governance (Q3).
Track 3: yes, as a first-class supported deployment mode. Disconnected operation runs via Azure Local Disconnected Operations, minimum version 2604.3.0, with dependencies arriving as expansion packs imported into a local edgeartifacts registry rather than pulled from the internet. Agentic Retrieval supports it too: "You can run the platform in disconnected environments without internet connectivity, by using a deployment model consistent with connected scenarios." Sources: SPIKE-19's citation of Prepare to deploy in disconnected environments, Agentic Retrieval overview.
One honest qualifier for track 3: even in disconnected mode, Agentic Retrieval "sends only system metadata and organizational identifiable information like subscription ID and cluster names to Microsoft. All customer content ... always stays in the on-premises infrastructure." Source: Agentic Retrieval overview. Customer data stays local; some control-plane metadata does not, in connected operation.
Q12. Management and UI surface
Track 1: two portals plus CLI plus control-plane REST. The Azure portal for the resource, the Foundry portal at ai.azure.com for models, deployments, content filters, agents, and the playground, az cognitiveservices for the resource and its deployments, and ARM control-plane REST including the quota-tier API. Sources: Content filtering (the portal path for creating and applying filters), Quotas and limits (the control-plane quota API), docs/guide/using-your-deployment.md and docs/guide/building-agents.md (the CLI and portal paths this repository actually uses).
Track 2: CLI and SDK only, no portal, and Microsoft is clear the CLI is not the product. "The optional web server and CLI are available for development workflows, but the core product is the local AI runtime and SDK that you integrate directly into your application." Source: What is Foundry Local?. The foundry CLI is in public preview. Source: Foundry Local CLI reference.
Track 3: more surfaces than the hub currently credits it with. Four, in fact:
- Azure portal, via Arc, for the cluster and for creating the extensions (
az k8s-extension create"or portal," per SPIKE-19's citation of the deploy page). kubectland Helm, for the prerequisite and intent layers.- A REST control plane with an interactive UI: "An auto-generated OpenAPI specification is available at
/openapi.jsonwith an interactive Swagger UI at/docs," on the control plane (port 8080) and on the predictive server (port 8000). The chat server has neither. Source: Foundry inference API reference. - If Agentic Retrieval is installed, a "Developer portal - configure ingestion settings, tune search parameters, and test queries through a local web interface" and a built-in chat UI. Source: Agentic Retrieval overview.
The hub's current cell for track 3, "Azure portal via Arc for the cluster. No Foundry portal surface," is true as far as it goes but should be widened: there is no ai.azure.com Foundry portal for track 3, and there are nonetheless three local UI or API surfaces beyond the Azure portal.
Q13. Structured outputs, tool calling, and vision input at the API level
Stated at the API level, independent of whether any particular model supports them, as the tasking asks.
Structured outputs.
- Track 1: yes, and fully specified.
response_formatof typejson_schemawithstrict: true, supporting a documented JSON Schema subset (string, number, boolean, integer, object, array, enum,anyOf),$defs, and recursive schemas, up to "100 object properties total, with up to five levels of nesting," with all fields required andadditionalProperties: falsemandatory. The older JSON mode remains as a separate, weaker feature that "guaranteed valid JSON but couldn't ensure strict adherence to the supplied schema." Two documented exclusions matter: structured outputs are not supported with parallel function calls (setparallel_tool_callstofalse), and not supported with Foundry Agents Service. Source: How to use structured outputs. - Track 2: not documented.
response_formatappears on the transcription endpoint only, not on chat completions. Source: REST API Reference. - Track 3: not documented. No
response_formator JSON-schema parameter appears in the chat completions payload reference. Source: Inference API endpoints and payload reference.
Tool and function calling.
- Track 1: yes. Up to 128 tools per request,
strict: trueon a function schema for structured tool arguments,parallel_tool_calls, and the full platform tool catalog behind Agent Service. Sources: Quotas and limits, Structured outputs, Agent Service overview. - Track 2: yes at the API level.
tools, plus the legacyfunctionsandfunction_callfields, and a per-modelsupportsToolCallingflag inGET /foundry/listso a client can check before it tries.finish_reasoncan befunction_call. Source: REST API Reference. - Track 3: yes at the API level, conditioned on the model. "When using models with tool calling capabilities, include the
tool_choicefield in the request payload." Source: Inference API endpoints and payload reference.
Vision input.
- Track 1: yes, images in the messages array, bounded at "50" images per request for
GPT-4oandGPT-4.1. Source: Quotas and limits. - Track 2: not documented on chat completions. The documented
messages[].contentis "(string) The actual message text," with no content-part array and no image type. Source: REST API Reference. This is a documentation absence, not a proven rejection, and SPIKE-22 is already the named owner of the vision question for both local tracks. - Track 3: not documented on chat completions. Image input is documented only on
/v1/predict, the ONNX predictive endpoint, as a base64items[]payload withcontent_type: image/jpeg, which is classification and scoring, not multimodal chat. Source: Inference API endpoints and payload reference. Agentic Retrieval does image retrieval and explicitly "isn't a visual language model (VLM)." Source: Agentic Retrieval overview.
What is still UNKNOWN
| # | Unknown | Why it is not in the docs | What resolves it |
|---|---|---|---|
| 1 | Does track 2 actually serve a Responses API endpoint? The product overview claims support for "the OpenAI Responses API format," but no Responses endpoint appears in the REST API reference. | The two pages disagree, and the REST reference carries an explicit "under active development and may include breaking changes without notice" caution. | Call GET /openai/status on a running instance, then POST /v1/responses with a minimal body and record the status code. Needs the owner-authorized install test already scoped in ADR-0013 decision 11; it adds no new authorization. |
| 2 | Does a stock OpenAI SDK client work against a track 3 endpoint? Every first-party track 3 example is curl, PowerShell, or kubectl run. No language SDK is documented. | Microsoft documents the wire protocol (OpenAI-compatible, api-key header described as the OpenAI-compatible form) but publishes no SDK sample for this target. | Point an OpenAI(base_url=..., api_key=...) client at a deployed model's gateway address and make one call. Requires a track 3 deployment, which does not exist; gated behind ADR-0014's preview access request. |
| 3 | Is there genuinely NO content filtering on tracks 2 and 3, or is it merely undocumented? | This is negative evidence across nine first-party pages. Microsoft nowhere states "there is no content filter," it simply never mentions one. | Two options, neither of which this spike can perform. Read the deployed extension's pod set for any classifier sidecar (kubectl get pods -n foundry-local-operator) on track 3. On track 2, send a prompt that track 1's filter blocks with HTTP 400 and observe whether the local endpoint returns a completion. Until then the governing assumption must be "assume none," because that is the safe direction of error. |
| 4 | Does track 3's chat server impose any request limit, concurrency cap, or 429 behaviour? | The predictive server's queue-full response is documented with queue_depth and retry_after; the chat server's error format is documented but no rate-limit condition is. | Read the chat server's behaviour under load against a deployed ModelDeployment with replicas: 1, or find a first-party statement. Needs a track 3 deployment. |
| 5 | Whether track 2's tools field behaves as the current OpenAI tools contract or as the legacy functions contract. The reference documents both, and describes tools only as "(object, optional) Tools calculated for the request," which is neither a type nor a schema. | The reference is terse on this field and the API is explicitly volatile. | Send a request with a current-shape tools array against a model whose catalog entry reports supportsToolCalling: true, and inspect whether the response returns tool_calls or function_call. Same install test as UNKNOWN #1. |
| 6 | Whether tracks 2 and 3 accept vision content parts on /v1/chat/completions for a multimodal model. | Documented content is a string on track 2, and track 3's payload reference shows text only. Neither says image content parts are rejected. | Already owned by SPIKE-22, which holds the vision question for both local tracks. Test by sending an image content part against a multimodal catalog entry, once one is identified. |
| 7 | What control, if any, should govern agent MCP traffic on track 3's Agentic Retrieval extension. ADR-0012 selects an APIM AI gateway for track 1 and it has no target on track 3. | ADR-0012 was written before this spike identified that track 3 has an MCP server at all. | A follow-on decision. The candidate controls documented for Agentic Retrieval are Entra ID integration, Azure RBAC, and the EdgeRAGDeveloper role for registering MCP servers as knowledge sources. Whether those are sufficient is a design question, not a research one. |
UNKNOWNs 1, 3 (track 2 half), and 5 all close from the single owner-authorized install test that ADR-0013 decision 11 already scopes, so they add no new gate. UNKNOWNs 2, 3 (track 3 half), and 4 all need a track 3 deployment, which is gated behind ADR-0014's preview access request. UNKNOWN 6 belongs to SPIKE-22. UNKNOWN 7 is a decision, not a measurement.
Recommendation
Lift the four tables below into
docs/targets/index.mdand retire everyUNKNOWN (SPIKE-31)cell. Nine of the ten cells the hub currently marksUNKNOWN (SPIKE-31)are now answered from first-party sources. The tenth (content safety on the local tracks) is answered as "none documented" with the negative-evidence caveat stated in the cell, which is a better outcome than an unqualifiedUNKNOWNbecause it tells an adopter what to assume.Correct the hub's track 3 agent cell rather than filling it in as written. "Foundry Agent Service" on track 3 is a No, but a bare No misleads, because a first-party on-premises agent runtime with a built-in MCP server does exist as a separate Arc extension. The cell should say both things. This is the finding most likely to change someone's target choice.
Raise the content-safety gap to a decision, not a table cell. ADR-0007 is the controlling safety decision for sensitive-audience content, and it assumes a filter that exists only on track 1. Before any content governed by ADR-0007 is served from track 2 or track 3, a compensating control has to be decided: a pre-filter or post-filter in the calling application, a separate Azure AI Content Safety call (which reintroduces a cloud dependency and therefore defeats part of the point of a local track), or an explicit scope statement that sensitive-audience content never runs locally. This spike recommends the third as the default, because it is the only one that costs nothing and cannot be misconfigured. Record it as an amendment to ADR-0007 or as a new ADR; do not leave it implicit in a comparison table.
Record in ADR-0014 that track 3's API key path bypasses Azure RBAC. "API key authentication grants full access without role checks" is a stronger statement than ADR-0014 currently carries, and it converts decision 5 (Entra ID as the authentication mode) from a preference into a security requirement. The keys are also readable and rotatable over the control-plane API by anyone holding
deployments/read, which is worth naming.Add track 3's two API planes to ADR-0014 and the track 3 target page. ADR-0014 describes the deployment layers correctly and says nothing about the runtime API surface. There is a control plane (port 8080,
/api/v1, full CRUD over models, deployments, and keys, RBAC-gated per verb, with Swagger UI at/docs), a chat server (port 5000), and a predictive server (port 8000). A future implementer needs that map, and the control plane in particular is a governance surface, because it is where keys are read and rotated and where deployments can be created outside the registry-driven flow ADR-0014 decision 6 mandates. That last point interacts directly with ADR-0014 decision 7's drift detection: a deployment created through the control-plane API is exactly the hand-createdModelDeploymentthat decision 7 exists to catch.Do not describe the three tracks as "OpenAI-compatible" without qualification anywhere in this documentation set. All three are, at the level of
POST /v1/chat/completionswithmodelandmessages. None of the three agrees with the others on whatmodelmeans, and only track 1 documents structured outputs, embeddings, images, speech synthesis, batch, or a Responses API. The compatibility is real and it is shallow, and a reader who ports an application on the strength of the phrase alone will discover the difference at the fourth feature, not the first.Leave the throughput questions where they are. SPIKE-18 UNKNOWN #2 (track 2 CPU tokens per second) and SPIKE-19 UNKNOWN #7 (track 3 CPU latency on a
Standard_D8s_v3worker) are both unmeasured and neither is closable from documentation. This spike deliberately records no number for either, and the parity tables say "bounded by the host" and "bounded by the cluster" rather than inventing a figure.
Verdict: the tracks are not substitutes, and the tables should say so
Stated plainly, because a parity spike that ends in "it depends" is not an answer.
Track 1 is a platform. Tracks 2 and 3 are model servers, and they are not the same model server as each other.
- Track 1 has, and neither local track has: image generation, speech synthesis, embeddings, a Batch API, a fine-tuning service, Foundry Agent Service, structured outputs, documented vision input, a quota system with a movable cap, and a mandatory content filter. Nine capabilities, each of them load-bearing for some workload.
- Track 2 and track 3 differ from each other more than the shared "Foundry Local" name suggests. Track 2 has no authentication at all and is documented as single-user by design. Track 3 requires authentication on every endpoint, resolves Entra authorization to Azure RBAC, and is documented as built for concurrent multi-node serving. On the two axes an enterprise cares about most, identity and concurrency, they are opposites.
- The one genuine three-way parity is the wire protocol, and even that is shallow: same path, three different meanings for
model, three different feature sets on the request body.
The decision rule this produces, for the hub's "when to choose which" table: choose track 1 unless a specific, stated requirement forbids it (data residency, air gap, no metered cost, no Azure subscription). Choose track 3 when that requirement exists and the endpoint must be governed. Choose track 2 when that requirement exists, the endpoint does not need to be governed, and one host is enough. Never choose a local track for a workload that depends on a content filter, on image or speech generation, or on embeddings, because none of the three exists there.
No no-go is recorded against any track. All three are viable for the workloads they fit. What this spike rejects is the framing that they are interchangeable.
Comparison tables, for lifting into docs/targets/index.md
Every cell below is sourced from the Findings above. Cells that cannot be sourced read UNKNOWN with the reason, not a guess.
Table A: API surface and client contract
| Feature | Azure cloud (track 1) | Windows Server (track 2) | Azure Local (track 3) |
|---|---|---|---|
| OpenAI-compatible chat completions | Yes, /openai/v1/chat/completions | Yes, /v1/chat/completions | Yes, /v1/chat/completions |
| Base URL shape | https://<account>.openai.azure.com/openai/v1/ or .services.ai.azure.com/openai/v1/ | http://localhost:<dynamic-port>/v1/. Never hardcode the port; discover it from GET /openai/status. | https://<gateway>/<deployment-name>/v1/, or the in-cluster service on port 5000 |
What goes in the model field | Your deployment name, not the vendor model id | The local model id as loaded, for example Phi-4-mini-instruct-generic-cpu | The catalog model id with version suffix, for example Phi-4-generic-cpu:1. The deployment name is in the URL path, not the body. |
api-version required | No, not with the v1 GA API. Images are the exception and want api-version=preview. | No such parameter | No such parameter |
| Responses API | Yes, and it is the recommended entry point for Azure OpenAI models | UNKNOWN. The product overview claims Responses-format support; the REST reference documents no Responses endpoint. | No |
| Streaming | Yes | Yes, stream: true, server-sent events ending data: [DONE] | UNKNOWN. Not stated in the payload reference. The control plane records a per-model capabilities.streaming flag. |
| Embeddings endpoint | Yes, /openai/v1/embeddings | No | No |
| Image generation endpoint | Yes, on the openai.azure.com hostname only | No | No |
| Speech synthesis endpoint | Yes, the regional Speech endpoint (not a model deployment) | No | No |
| Speech to text endpoint | Yes | Yes, /v1/audio/transcriptions (Whisper) | Yes, /v1/audio/transcriptions |
| Predictive (ONNX classification) endpoint | No | No | Yes, /v1/predict on the predictive server |
| Model listing | Yes, via az cognitiveservices account deployment list and the v1 models surface | Yes, GET /openai/models, GET /openai/loadedmodels, GET /foundry/list | Yes, GET /v1/models (data plane) and GET /api/v1/models (control plane) |
| Model lifecycle over the API | Deployments are ARM resources, not data-plane calls | Yes, /openai/download, /openai/load/{name}, /openai/unload/{name}, with ttl and ep | Yes, full deployment CRUD on the control plane at /api/v1/namespaces/{ns}/deployments |
| API stability | GA, with preview features opted into by header or alpha path segment | Preview, and explicitly volatile: "under active development and may include breaking changes without notice" | Preview, no SLA, no GA date |
Table B: SDKs, authentication, and identity
| Feature | Azure cloud (track 1) | Windows Server (track 2) | Azure Local (track 3) |
|---|---|---|---|
| Stock OpenAI SDK | Yes, documented for Python, C#, JavaScript, Go, and Java against the v1 base URL | Yes, documented, paired with the Foundry Local SDK in every sample | UNKNOWN. Wire-compatible, but no first-party SDK sample exists. Every documented example is curl, PowerShell, or kubectl. |
| First-party SDK | Azure.AI.OpenAI, azure-ai-projects (required for Agent Service) | foundry-local-sdk (Python, JS, Rust), Microsoft.AI.Foundry.Local (C#), plus -winml variants on Windows | None documented |
| Client configuration difference | Base URL plus a key or a token provider. Token refresh is handled by the OpenAI() client. | The SDK must start the service, download and load the model, and hand you a dynamic port before any client can connect | Gateway address plus a bearer credential. Self-signed certificates in the documented examples require skipping certificate validation. |
| Endpoint authentication | Microsoft Entra ID bearer token, or an API key accepted in either the api-key or Authorization: Bearer header | None. No auth header is documented on any endpoint. Microsoft's own samples pass "none" and "notneeded" as the key. | Required on every endpoint. API key (Authorization: Bearer or api-key) or Entra ID JWT. |
| Azure RBAC on the endpoint | Yes. Cognitive Services OpenAI User or equivalent. | No. "Azure RBAC: Not applicable (runs locally)." | Yes, for the Entra path: Cognitive Services OpenAI User (or equivalent) at cluster scope. The API key path grants full access with no role check. |
| Key storage | Azure-managed account keys, referenced by name from the vault | Not applicable, no key exists | A Kubernetes Secret per deployment, readable and rotatable over the control-plane API. No Key Vault integration documented. |
| Control-plane authorization | Azure RBAC on the ARM resource | Not applicable, nothing is projected into Azure | Azure RBAC per verb: deployments/read for GET and HEAD, deployments/write for POST, PUT, and PATCH, deployments/delete for DELETE |
| What does NOT exist | Nothing material | Entra ID, Azure RBAC, API keys, Key Vault, per-caller identity, token expiry | A Key Vault path for the operator's own keys |
Table C: Platform features
| Feature | Azure cloud (track 1) | Windows Server (track 2) | Azure Local (track 3) |
|---|---|---|---|
| Foundry Agent Service | Yes. Prompt agents and hosted agents, both via the Responses API on a project endpoint. | No | No, but a separate Arc extension (Agentic Retrieval in Foundry Local, preview) provides an on-premises agent runtime with threads, runs, and a Foundry Agents API |
| Agent frameworks can target it | Yes, natively (Agent Framework, LangGraph, OpenAI Agents SDK, Anthropic Agent SDK, GitHub Copilot SDK) and by pointing any framework at the v1 base URL | Yes, bring-your-own-runtime only. LangChain is named by Microsoft. | Yes in principle, bring-your-own-runtime, subject to the SDK UNKNOWN in Table B |
| MCP tool calling | Yes. Remote MCP servers from the tool catalog, plus Toolbox as a single MCP-compatible endpoint. | No MCP surface documented | Yes, only via the Agentic Retrieval extension: a built-in MCP server with six search tools, plus connections to external MCP servers |
| MCP tool gateway (ADR-0012) | Applies. APIM AI gateway, gated to a future agent phase, nothing provisioned. | Does not apply. No Foundry resource, no agents, no MCP tools to govern. | Does not apply. The endpoint is a Kubernetes service, not a Foundry resource, so resource-level enablement has no target. Agentic Retrieval's MCP server needs its own control decision, which is not yet made. |
| RAG and retrieval | Bring your own, plus Azure AI Search, the file-search tool, and a Groundedness filter | Bring your own, with no retrieval primitive at all and no way to generate embeddings locally | Agentic Retrieval: ingestion, chunking, embeddings, Milvus vector collections, hybrid/vector/text/multimodal search, per-collection RBAC. Separate extension, separate preview. |
| Agentic Retrieval GPU and model requirements | Not applicable | Not applicable | Recommended language model is GPT-OSS-20B, which "requires its own GPU," plus two more GPUs for text embedding (BGE-M3) and image embedding (CLIP ViT-L/14). Entra ID auth must be enabled at Foundry Local install time or it cannot connect. |
| Fine-tuning | Yes. Supervised, DPO, and reinforcement fine-tuning, with published job and file limits. | No fine-tuning service. Bring a model and convert it with Olive to ONNX. | No fine-tuning service. Bring your own model from an OCI registry. |
| Distillation | Stored completions (the distillation capture mechanism) are supported. A separate named distillation surface is not documented. | No | No |
| Batch inference | Yes. Global Batch and Data Zone Batch, quota counted in enqueued tokens. | No. Microsoft states Foundry Local does not provide continuous batching. | No batch job API. vLLM is the high-throughput serving runtime, which is not the same thing. |
| Asynchronous processing | Yes, Batch plus Responses API background tasks | No | Only the predictive server's request queue, which returns queue_depth and retry_after when full |
Structured outputs (response_format: json_schema) | Yes, with a documented JSON Schema subset, strict: true, $defs, and recursion. Not supported with parallel tool calls or with Agent Service. | Not documented. response_format appears only on the transcription endpoint. | Not documented |
| JSON mode | Yes, as the older and weaker predecessor to structured outputs | Not documented | Not documented |
| Tool and function calling (API level) | Yes. Up to 128 tools per request, strict function schemas, parallel_tool_calls. | Yes. tools, plus legacy functions and function_call. Per-model supportsToolCalling flag in the catalog. | Yes, for tool-capable models, via tool_choice |
| Vision input on chat completions (API level) | Yes. Up to 50 images per request on GPT-4o and GPT-4.1. | Not documented. content is documented as a string with no content-part array. See SPIKE-22. | Not documented on chat. Image input exists only on /v1/predict (ONNX classification). Agentic Retrieval does image retrieval and "isn't a visual language model." |
Table D: Safety, limits, operation, and management
| Feature | Azure cloud (track 1) | Windows Server (track 2) | Azure Local (track 3) |
|---|---|---|---|
| Content filtering | Yes, on by default. Prompts and completions run through an Azure AI Content Safety ensemble. Four categories (hate and fairness, sexual, violence, self-harm) at four severity levels. | None documented. No content filter, guardrail, classifier, or blocklist appears anywhere in the Foundry Local documentation. Assume none. | None documented. Not in the key-capability list, the architecture summary, or either API reference. Assume none. |
| Child-safety coverage specifically | The sexual category explicitly covers "Child exploitation, child abuse, child grooming" | None. Whatever the open-weight model was trained with is the only control. | None. Whatever the open-weight model was trained with is the only control. |
| Optional additional filters | Groundedness, protected material (text and code), PII, prompt shields for user and indirect prompt attacks, task adherence, blocklists | None | None |
| Turning filtering off | Gated. "No filters" and "Annotate only" require approval for modified content filtering. | Not applicable, nothing to turn off | Not applicable, nothing to turn off |
| Filtered-request behaviour | Prompt filtered: HTTP 400 with code: content_filter. Completion filtered: HTTP 200 with finish_reason: content_filter. | Not applicable | Not applicable |
| Known filtering gap | Audio models such as Whisper are not covered. The filter can also fail open, signalled by an error object in content_filter_results. | Not applicable | Not applicable |
| Quotas and rate limits | Yes. Subscription-scoped, seven tiers, published RPM and TPM per model and deployment type, auto-upgrading with usage. | None. Bounded by the host: cores, memory, execution provider, and model size. | None per caller. Bounded by the cluster: replicas (1 to 100), CPU, memory, and GPU requests and limits, node capacity, and runtime choice. |
| 429 behaviour | Yes, and it can occur even when token metrics look under quota. An entry left at the capacity fallback of 1 measures at roughly one request per minute, which breaks agentic callers rather than protecting spend. | Not applicable | UNKNOWN for the chat server. The predictive server returns a queue-full payload with Retry-After. |
| Structural limits worth knowing | 32 standard deployments per resource, 30 resources per subscription, 128 tools and 2,048 messages per chat request | None published | replicas 1 to 100, resources.limits.gpu 1 to 8, models list page size clamped to 100 |
| Concurrent multi-user serving | Yes, by design | No, by design. "Optimized for hardware-constrained devices where a single user accesses the model at a time ... it isn't designed as a server inference stack." | Yes, by design. "Scale inference across multi-node Kubernetes clusters for concurrent usage." |
| Disconnected or air-gapped operation | No | Yes, once the model cache is populated. Network is used only for model and component downloads and optional diagnostics. | Yes, via Azure Local Disconnected Operations 2604.3.0 or later, with dependencies imported as expansion packs. Some control-plane metadata still flows in connected operation. |
| Portal or UI surface | Azure portal and the Foundry portal (ai.azure.com), including the content-filter and agent authoring experience | None. CLI and SDK only, and Microsoft describes the CLI as a development-workflow tool, not the product. | Azure portal via Arc for the cluster and extensions. Swagger UI at /docs on the control plane and predictive server. A developer portal and chat UI if Agentic Retrieval is installed. No ai.azure.com Foundry portal. |
| Local CLI | Not required. az for the resource. | foundry CLI (preview) | kubectl, Helm, and the Azure CLI (az aksarc, az k8s-extension) |
| Management API | ARM control plane, plus the quota-tier API | The local REST surface (/openai/*, /foundry/list) | A dedicated FastAPI control plane on port 8080 at /api/v1, RBAC-gated per verb, with an auto-generated OpenAPI spec |
Sources
All first-party Microsoft unless noted. Retrieved 2026-07-30.
Track 1 (Azure AI Foundry in Azure)
- Azure OpenAI in Microsoft Foundry Models v1 API (v1 base URL,
api-versionno longer required,OpenAI()client across five languages, preview-header opt-in, model support, changelog including Batch, structured outputs, and stored completions): https://learn.microsoft.com/azure/foundry/openai/api-version-lifecycle - Quotas and limits (subscription-scoped quota, seven tiers, per-model RPM and TPM, structural limits, batch limits and enqueued-token quota, fine-tuning job and file limits, 50 images per request, 429 guidance): https://learn.microsoft.com/azure/foundry/openai/quotas-limits
- Content filtering for Microsoft Foundry Models (the filter ensemble, four categories and four severities, the child-exploitation enumeration, optional filters, configurability and the approval gate, the audio-model exclusion, the fail-open behaviour, HTTP 400 and
finish_reason: content_filter): https://learn.microsoft.com/azure/foundry-classic/foundry-models/concepts/content-filter - How to use structured outputs (
response_format: json_schema,strict: true, the supported JSON Schema subset, nesting limits, the parallel-tool-call and Agent Service exclusions, supported models, API support): https://learn.microsoft.com/azure/foundry/openai/how-to/structured-outputs - Fine-tuning (supervised, DPO, and reinforcement fine-tuning, and per-model support): https://learn.microsoft.com/azure/foundry/openai/how-to/fine-tuning
- How to use global batch processing (the Batch API and its operating model): https://learn.microsoft.com/azure/foundry/openai/how-to/batch
- What is Microsoft Foundry Agent Service? (prompt and hosted agents, the Responses API as single entry point, the hosted-agent framework list, the tool catalog, remote MCP servers and their four auth options, Toolbox, agent identity, content safety): https://learn.microsoft.com/azure/foundry/agents/overview
- Govern MCP tools by using an AI gateway (preview) (the feature ADR-0012 adopts): https://learn.microsoft.com/azure/foundry/agents/how-to/tools/governance
- Configure AI Gateway in your Foundry resources (resource-level enablement, the APIM prerequisite): https://learn.microsoft.com/azure/foundry/configuration/enable-ai-api-management-gateway-portal
Track 2 (Foundry Local on Windows Server)
- Foundry Local REST API Reference (every documented endpoint, the chat request and response body including
stream,tools,functions,function_call,ep,ttl, the string-onlycontent,response_formaton transcription only,supportsToolCallingin the catalog listing, the dynamic-port warning, the breaking-change caution): https://learn.microsoft.com/azure/foundry-local/reference/reference-rest - What is Foundry Local? (product framing, the Responses-format claim, the curated catalog scope, the optional local server and LangChain, the "Can Foundry Local run on a server?" answer, the single-user statement, the no-concurrent-batching statement, offline operation, no Azure subscription, supported platforms): https://learn.microsoft.com/azure/foundry-local/what-is-foundry-local
- Integrate with inference SDKs (the four language SDKs and their Windows and cross-platform variants, the stock OpenAI client paired with the SDK, the
"none"and"notneeded"placeholder keys, the start-load-call-unload lifecycle): https://learn.microsoft.com/azure/foundry-local/how-to/how-to-integrate-with-inference-sdks - Foundry Local CLI reference ("Azure RBAC: Not applicable (runs locally)," preview status, prerequisites): https://learn.microsoft.com/azure/foundry-local/reference/reference-cli
- Best practices and troubleshooting guide for Foundry Local CLI (preview) (slow-CPU-inference guidance and its hardware remedy, per-model licence check, cache encryption for sensitive fine-tuning data): https://learn.microsoft.com/azure/foundry-local/reference/reference-best-practice
- Compile Hugging Face models and run on Foundry Local (Olive conversion and optimization,
--device,--provider,--precision,inference_model.json): https://learn.microsoft.com/azure/foundry-local/how-to/how-to-compile-hugging-face-models
Track 3 (Foundry Local on Azure Local)
- What is Foundry Local on Azure Local? (key capabilities, architecture summary, the two auth modes, supported workloads, supported regions, concurrent multi-node scaling, disconnected operation, preview by request): https://learn.microsoft.com/azure/azure-sovereign-clouds/private/foundry-local/overview
- Inference API endpoints and payload reference (the six data-plane endpoints, the
tool_choicestatement, all three accepted auth header formats, "All endpoints require authentication," the 401 behaviour, the/v1/predictbase64 image payload): https://learn.microsoft.com/azure/azure-sovereign-clouds/private/foundry-local/reference-inference-api-endpoints-payload - Foundry inference API reference (the three services and their ports, the control-plane base path and full CRUD surface, the per-verb RBAC split, the models and deployments and keys endpoints, the
fndry-pk-key format and rotation, the SSRF validation on custom registries, the predictive queue-full payload, Swagger UI at/docs, no gRPC): https://learn.microsoft.com/azure/azure-sovereign-clouds/private/foundry-local/reference-inference-api - Run inference on Foundry Local on Azure Local (the gateway and in-cluster URL shapes, the versioned
modelvalue, both auth options, theCognitive Services OpenAI Userrequirement for Entra, "API key authentication grants full access without role checks," the BYO model manifest with an OCI registry andsecretRef): https://learn.microsoft.com/azure/azure-sovereign-clouds/private/foundry-local/how-to-run-inference - ModelDeployment and operator configuration reference (spec fields, replica range, resource defaults, GPU limits): https://learn.microsoft.com/azure/azure-sovereign-clouds/private/foundry-local/reference-model-deployment-operator
- Inference runtimes in Foundry Local on Azure Local (ONNX-GenAI on CPU or GPU, vLLM GPU-only and high-throughput): https://learn.microsoft.com/azure/azure-sovereign-clouds/private/foundry-local/concept-inference-runtimes
- Requirements for Foundry Local on Azure Local (CPU and GPU support, Entra-not-on-Helm, the TLS certificate requirement, worker sizing): https://learn.microsoft.com/azure/azure-sovereign-clouds/private/foundry-local/concept-requirements
- Deploy Foundry Local as an Azure Arc extension (the ordered install sequence, the Gateway API Inference Extension and Endpoint Picker,
api.exposure, the portal install path): https://learn.microsoft.com/azure/azure-sovereign-clouds/private/foundry-local/deploy-foundry-local-arc-extension - Prepare to deploy Foundry Local on Azure Local in disconnected environments (the
2604.3.0minimum and the expansion-pack model): https://learn.microsoft.com/azure/azure-sovereign-clouds/private/foundry-local/disconnected-operations/how-to-prepare - Agentic Retrieval and Agents and Tools with Foundry Local Overview (the three-component platform, agent execution, the built-in MCP server and external MCP support, threads and runs, the Foundry Agents API, SSE streaming, the two GPU embedding models, the developer portal and chat UI, the three deployment modes, "isn't a visual language model," per-collection RBAC, the
EdgeRAGDeveloperrole, disconnected support, the metadata-versus-content statement): https://learn.microsoft.com/azure/azure-arc/agents-tools-foundry-local/overview - What you need for Agentic Retrieval in Foundry Local (the GPT-OSS-20B recommendation and its dedicated-GPU requirement): https://learn.microsoft.com/azure/azure-arc/agents-tools-foundry-local/requirements
Local records in this repository (not first-party Microsoft, cited as this project's own verified record)
docs/targets/index.md(the comparison hub this spike fills, and the source of the track numbering and slugs)docs/research/SPIKE-08-foundry-local-on-device.md,docs/research/SPIKE-09-azure-local-foundry.md,docs/research/SPIKE-18-foundry-local-windows-server.md,docs/research/SPIKE-19-foundry-local-azure-local-deployment.mddocs/adr/ADR-0011-multi-target-deployment-automation.md,docs/adr/ADR-0012-agent-mcp-gateway-governance.md,docs/adr/ADR-0013-foundry-local-windows-server-install.md,docs/adr/ADR-0014-foundry-local-azure-local-deployment-layers.mddocs/guide/using-your-deployment.md(the track 1 feature surface, including the image-hostname difference and the404 DeploymentNotFoundbehaviour, both of which were found by executing the calls against a real deployment rather than by reading documentation)docs/guide/building-agents.md(the track 1 agent surface and the project endpoint shape)
No endpoint of any kind was called during this spike, and no az command was run. Every track 1 behaviour attributed to a live deployment is quoted from this repository's own previously executed and recorded samples, not re-tested here.