Last updated 2026-05-17
Azure OpenAI Provider
The azureopenai provider talks to Azure-hosted OpenAI
deployments. The request and response shapes are OpenAI-compatible;
the differences from the
OpenAI provider are
the endpoint URL (deployment-scoped), the auth header
(api-key instead of Authorization: Bearer),
and a mandatory api-version query parameter.
Capability-wise it matches OpenAI: chat
(Provider),
embeddings
(Embedder),
TTS (Speaker),
and Whisper STT
(Transcriber).
Import path
import ( "github.com/elloloop/llmrouter" "github.com/elloloop/llmrouter/providers/azureopenai")Construction
Azure requires the resource name, the deployment name, and the API version. The library models these as required options:
p, err := azureopenai.New( llmrouter.WithAPIKey(os.Getenv("AZURE_OPENAI_KEY")), azureopenai.WithResource("my-resource"), // {resource}.openai.azure.com azureopenai.WithDeployment("gpt-4o-deployment"), // your deployment name azureopenai.WithAPIVersion("2024-10-21"), // pinned API version)if err != nil { log.Fatal(err)}
Constructed endpoint:
https://{resource}.openai.azure.com/openai/deployments/{deployment}/chat/completions?api-version={version}.
For Microsoft Entra ID (formerly Azure AD) auth, omit
WithAPIKey and pass a custom http.Client
via WithHTTPClient that injects a bearer token from
azidentity.
Model vs. Deployment
Full example
package main
import ( "context" "fmt" "log" "os"
"github.com/elloloop/llmrouter" "github.com/elloloop/llmrouter/providers/azureopenai")
func main() { p, err := azureopenai.New( llmrouter.WithAPIKey(os.Getenv("AZURE_OPENAI_KEY")), azureopenai.WithResource(os.Getenv("AZURE_OPENAI_RESOURCE")), azureopenai.WithDeployment("gpt-4o"), azureopenai.WithAPIVersion("2024-10-21"), ) if err != nil { log.Fatal(err) }
stream, err := p.CompletionStream(context.Background(), llmrouter.ChatRequest{ Model: "gpt-4o", Messages: []llmrouter.Message{ llmrouter.TextMessage("user", "Summarise the Azure OpenAI deployment model in one sentence."), }, }) if err != nil { log.Fatal(err) }
for chunk := range stream.Chunks() { for _, c := range chunk.Choices { fmt.Print(c.Delta.Content) } } if err := stream.Err(); err != nil { log.Fatal(err) } fmt.Println()}Embeddings
Azure exposes embedding models via separate deployments. Construct a second provider pinned to your embedding deployment:
embed, _ := azureopenai.New( llmrouter.WithAPIKey(os.Getenv("AZURE_OPENAI_KEY")), azureopenai.WithResource(resource), azureopenai.WithDeployment("text-embedding-3-large"), azureopenai.WithAPIVersion("2024-10-21"),)
resp, err := embed.Embed(ctx, llmrouter.EmbedRequest{ Model: "text-embedding-3-large", Inputs: []string{"hello", "world"}, Dimensions: 1024,})Audio (TTS / Whisper)
Both Speak and Transcribe are available
when the deployment is for a TTS model
(tts-1, tts-1-hd) or for Whisper
(whisper-1) respectively. The behaviour matches the
OpenAI provider —
only the auth and endpoint differ.
Error handling
Non-2xx responses surface as
*llmrouter.ErrUpstream
with Provider == "azureopenai". Common cases:
401— missing or wrongapi-key.404— deployment not found (check resource name + deployment name).429— Azure capacity exhausted; honourRetry-After.400— content filter (Azure's prompt or response policy triggered).
Caveats
- One provider per deployment. Azure splits chat and embedding and TTS into separate deployments — construct one provider per deployment you need.
- Content filter. Azure may return 400 with a
content-filter body even when the upstream OpenAI model would
have responded. Inspect
ErrUpstream.Body. - Region pinning. Pin
api-versionto a known-good value; Azure rolls forward and old versions eventually 404.
See also
- OpenAI provider — same wire format, different auth.
- Configuration & Options — option pattern.