A reproducible walkthrough that:
- Provisions a Foundry / Azure OpenAI GlobalStandard deployment of
gpt-4.1(2025-04-14) in westeurope (non-US, non-Sweden). - Enables priority processing on that deployment via the ARM REST API
(
properties.serviceTier = "Priority", api-version2025-12-01). - Exercises every documented condition under which a request flagged as
priority will not actually be served by the priority tier, and
inspects
response.service_tierto prove the behavior.
⚠️ Two surfaces, two spellings. The control-plane (ARM) schema uses camelCaseserviceTierwith PascalCase valuesDefault/Priority. The data-plane (OpenAI chat/responses APIs) uses snake_caseservice_tierwith lowercase valuesdefault/priority/auto. They mean the same thing on different planes; getting the casing wrong is the #1 way to hitInvalidRequestContent: could not be deserialized.
All operational commands live in the Makefile. This README is purely conceptual.
Authoritative source for everything below: Enable priority processing for Microsoft Foundry models
A request-routing tier on top of the existing pay-as-you-go Azure OpenAI deployments. It is not a separate SKU and it does not reserve capacity (that's PTU). It shares the standard TPM quota pool, but binds the deployment — or an individual request — to a latency SLO instead of best-effort treatment:
| Model | Latency target (p50, 5-min window) |
|---|---|
| gpt-5.5, 2026-04-24 | 99% > 100 tokens/sec |
| gpt-5.4, 2026-03-05 | 99% > 50 tokens/sec |
| gpt-5.2, 2025-12-11 | 99% > 50 tokens/sec |
| gpt-5.1, 2025-11-13 | 99% > 50 tokens/sec |
| gpt-4.1, 2025-04-14 | 99% > 80 tokens/sec |
The classic trade-off:
- PTU — pay for reserved capacity, predictable latency, no per-request fees.
- Priority processing — pay-as-you-go, latency SLO, no commitment, but the service reserves the right to downgrade you under specific conditions.
- Standard — pay-as-you-go, no latency SLO.
Only on these deployment types:
GlobalStandardDataZoneStandard(US data zone only — EU data zone is unsupported)
Regional Standard deployments are not eligible. The region table in
the official docs lists ~28 priority-eligible regions for GlobalStandard.
This demo picks westeurope to satisfy "non-US, excluding Sweden".
You set service_tier in two independent places. The cross-product
determines which tier ultimately processes a request:
Deployment-level service_tier |
Request-level service_tier |
Effective tier |
|---|---|---|
default |
auto (or unset), default |
Standard |
default |
priority |
Priority |
priority |
auto (or unset), priority |
Priority |
priority |
default |
Standard (override) |
The response body echoes the tier that actually served the request in
response.service_tier — that's the only ground truth.
These are the cases the test scripts deliberately exercise. Each has a
corresponding make tests.* target:
| # | Condition | Documented behavior | Demo target |
|---|---|---|---|
| 1 | Request body sets service_tier=default while deployment is priority |
Truth table forces Standard | make tests.override-default |
| 2 | gpt-4.1 prompt > 128k tokens (auto-scales the deployment to ≥ 200k TPM first so the rate limiter can't mask the result) | Either HTTP 400 (per preview troubleshooting table) or HTTP 200 with service_tier=default (downgrade) — docs document both |
make tests.long-context |
| 3 | Traffic grows > 50% TPM within < 15 min (ramp-rate limit) | Some priority requests routed to Standard at standard rates | make tests.ramp |
| 4 | Peak demand on the global priority pool | Same as #3 — probabilistic re-route to Standard | (manifests within tests.ramp) |
| 5 | Streaming via Responses API — intermediate chunks | Intermediate chunks may report incorrect service_tier; only the final chunk is authoritative |
make tests.streaming |
| 6 | Deployment is regional Standard or EU DataZoneStandard |
Control plane rejects priority on that SKU; cannot even be enabled | make tests.unsupported |
In addition, make tests.honored runs the baseline positive case
(deployment=priority + request unset → service_tier=priority).
A note on #3 / #4: these are probabilistic. At low baseline traffic, even a
30-request burst may stay entirely on the priority tier. The script tallies
priority vs default responses; observing any default proves the downgrade
path.
A note on #2: docs describe both "downgrade to standard" (GA) and "HTTP 400" (preview troubleshooting). The script accepts either as a documented outcome and reports which path actually fired.
Plain az cognitiveservices account deployment create does not currently
expose the serviceTier property, so the demo issues a raw control-plane
PUT via az rest. The minimum api-version that ships serviceTier on
Microsoft.CognitiveServices/accounts/deployments is 2025-12-01
(schema).
PUT /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.CognitiveServices
/accounts/{account}/deployments/{deployment}?api-version=2025-12-01
{
"sku": { "name": "GlobalStandard", "capacity": 250 },
"properties": {
"model": { "format": "OpenAI", "name": "gpt-4.1", "version": "2025-04-14" },
"serviceTier": "Priority"
}
}
After provisioning, the script reads back properties.serviceTier from
the same URI to confirm the control plane accepted the toggle.
The default capacity is 250k TPM (overridable via SKU_CAPACITY=...),
sized so the >128k-token long-context test can land a single request without
tripping the rate limiter. To resize an existing deployment in place
without recreating, use make scale CAPACITY=<k-TPM> — it round-trips
the full deployment document so serviceTier and model identity are
preserved.
Priority requests are billed at the priority rate; downgraded requests are
billed at the standard rate. The demo gpt-4.1 deployment is provisioned
with 250k TPM of quota (sized so the >128k-token long-context test fits in
a single request) and incurs charges only while requests are actually sent.
make cleanup removes the resource group and purges the soft-deleted
Foundry account name.
Out of scope for the test scripts, but per docs:
- Metrics — In the Azure portal, on the Azure OpenAI resource, add the
Azure OpenAI requestsmetric and apply splitting byServiceTierRequest/ServiceTierResponseto see priority vs standard request volumes. - Cost analysis — Filter by the
deploymentbilling tag in Azure Cost Management to separate priority and standard charges by deployment name.