Skip to content

Commit 805aeff

Browse files
authored
feat: add azure openai models (#74)
1 parent bce2ec5 commit 805aeff

File tree

10 files changed

+291
-11
lines changed

10 files changed

+291
-11
lines changed

README.md

+23-10
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ OpenCode is a Go-based CLI application that brings AI assistance to your termina
1111
## Features
1212

1313
- **Interactive TUI**: Built with [Bubble Tea](https://github.com/charmbracelet/bubbletea) for a smooth terminal experience
14-
- **Multiple AI Providers**: Support for OpenAI, Anthropic Claude, Google Gemini, AWS Bedrock, and Groq
14+
- **Multiple AI Providers**: Support for OpenAI, Anthropic Claude, Google Gemini, AWS Bedrock, Groq, and Azure OpenAI
1515
- **Session Management**: Save and manage multiple conversation sessions
1616
- **Tool Integration**: AI can execute commands, search files, and modify code
1717
- **Vim-like Editor**: Integrated editor with text input capabilities
@@ -66,15 +66,19 @@ OpenCode looks for configuration in the following locations:
6666

6767
You can configure OpenCode using environment variables:
6868

69-
| Environment Variable | Purpose |
70-
| ----------------------- | ------------------------ |
71-
| `ANTHROPIC_API_KEY` | For Claude models |
72-
| `OPENAI_API_KEY` | For OpenAI models |
73-
| `GEMINI_API_KEY` | For Google Gemini models |
74-
| `GROQ_API_KEY` | For Groq models |
75-
| `AWS_ACCESS_KEY_ID` | For AWS Bedrock (Claude) |
76-
| `AWS_SECRET_ACCESS_KEY` | For AWS Bedrock (Claude) |
77-
| `AWS_REGION` | For AWS Bedrock (Claude) |
69+
| Environment Variable | Purpose |
70+
|----------------------------|--------------------------------------------------------|
71+
| `ANTHROPIC_API_KEY` | For Claude models |
72+
| `OPENAI_API_KEY` | For OpenAI models |
73+
| `GEMINI_API_KEY` | For Google Gemini models |
74+
| `GROQ_API_KEY` | For Groq models |
75+
| `AWS_ACCESS_KEY_ID` | For AWS Bedrock (Claude) |
76+
| `AWS_SECRET_ACCESS_KEY` | For AWS Bedrock (Claude) |
77+
| `AWS_REGION` | For AWS Bedrock (Claude) |
78+
| `AZURE_OPENAI_ENDPOINT` | For Azure OpenAI models |
79+
| `AZURE_OPENAI_API_KEY` | For Azure OpenAI models (optional when using Entra ID) |
80+
| `AZURE_OPENAI_API_VERSION` | For Azure OpenAI models |
81+
7882

7983
### Configuration File Structure
8084

@@ -170,6 +174,15 @@ OpenCode supports a variety of AI models from different providers:
170174
- Deepseek R1 distill Llama 70b
171175
- Llama 3.3 70b Versatile
172176

177+
### Azure OpenAI
178+
179+
- GPT-4.1 family (gpt-4.1, gpt-4.1-mini, gpt-4.1-nano)
180+
- GPT-4.5 Preview
181+
- GPT-4o family (gpt-4o, gpt-4o-mini)
182+
- O1 family (o1, o1-mini)
183+
- O3 family (o3, o3-mini)
184+
- O4 Mini
185+
173186
## Usage
174187

175188
```bash

cmd/schema/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ func generateSchema() map[string]any {
174174
string(models.ProviderGemini),
175175
string(models.ProviderGROQ),
176176
string(models.ProviderBedrock),
177+
string(models.ProviderAzure),
177178
}
178179

179180
providerSchema["additionalProperties"].(map[string]any)["properties"].(map[string]any)["provider"] = map[string]any{

go.mod

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.24.0
55
toolchain go1.24.2
66

77
require (
8+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0
89
github.com/JohannesKaufmann/html-to-markdown v1.6.0
910
github.com/PuerkitoBio/goquery v1.9.2
1011
github.com/alecthomas/chroma/v2 v2.15.0
@@ -44,6 +45,9 @@ require (
4445
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
4546
cloud.google.com/go/compute/metadata v0.6.0 // indirect
4647
cloud.google.com/go/longrunning v0.5.7 // indirect
48+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
49+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
50+
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
4751
github.com/andybalholm/cascadia v1.3.2 // indirect
4852
github.com/atotto/clipboard v0.1.4 // indirect
4953
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
@@ -74,11 +78,13 @@ require (
7478
github.com/go-logr/logr v1.4.2 // indirect
7579
github.com/go-logr/stdr v1.2.2 // indirect
7680
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
81+
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
7782
github.com/google/s2a-go v0.1.8 // indirect
7883
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
7984
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
8085
github.com/gorilla/css v1.0.1 // indirect
8186
github.com/inconshreveable/mousetrap v1.1.0 // indirect
87+
github.com/kylelemons/godebug v1.1.0 // indirect
8288
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
8389
github.com/mattn/go-isatty v0.0.20 // indirect
8490
github.com/mattn/go-localereader v0.0.1 // indirect
@@ -89,6 +95,7 @@ require (
8995
github.com/muesli/cancelreader v0.2.2 // indirect
9096
github.com/ncruces/julianday v1.0.0 // indirect
9197
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
98+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
9299
github.com/pmezard/go-difflib v1.0.0 // indirect
93100
github.com/rivo/uniseg v0.4.7 // indirect
94101
github.com/rogpeppe/go-internal v1.14.1 // indirect

go.sum

+15
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4
1010
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
1111
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
1212
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
13+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
14+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
15+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
16+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
17+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
18+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
19+
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
20+
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
1321
github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5x+rHJnb1ssNmqpLH/k=
1422
github.com/JohannesKaufmann/html-to-markdown v1.6.0/go.mod h1:NUI78lGg/a7vpEJTz/0uOcYMaibytE4BUOQS8k78yPQ=
1523
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
@@ -113,6 +121,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
113121
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
114122
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
115123
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
124+
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
125+
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
116126
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
117127
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
118128
github.com/google/generative-ai-go v0.19.0 h1:R71szggh8wHMCUlEMsW2A/3T+5LdEIkiaHSYgSpUgdg=
@@ -140,6 +150,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
140150
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
141151
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
142152
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
153+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
154+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
143155
github.com/lrstanley/bubblezone v0.0.0-20250315020633-c249a3fe1231 h1:9rjt7AfnrXKNSZhp36A3/4QAZAwGGCGD/p8Bse26zms=
144156
github.com/lrstanley/bubblezone v0.0.0-20250315020633-c249a3fe1231/go.mod h1:S5etECMx+sZnW0Gm100Ma9J1PgVCTgNyFaqGu2b08b4=
145157
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@@ -177,6 +189,8 @@ github.com/openai/openai-go v0.1.0-beta.2 h1:Ra5nCFkbEl9w+UJwAciC4kqnIBUCcJazhmM
177189
github.com/openai/openai-go v0.1.0-beta.2/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y=
178190
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
179191
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
192+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
193+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
180194
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
181195
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
182196
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -295,6 +309,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
295309
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
296310
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
297311
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
312+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
298313
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
299314
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
300315
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

internal/config/config.go

+11
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,15 @@ func setProviderDefaults() {
272272
viper.SetDefault("agents.title.model", models.BedrockClaude37Sonnet)
273273
return
274274
}
275+
276+
if os.Getenv("AZURE_OPENAI_ENDPOINT") != "" {
277+
// api-key may be empty when using Entra ID credentials – that's okay
278+
viper.SetDefault("providers.azure.apiKey", os.Getenv("AZURE_OPENAI_API_KEY"))
279+
viper.SetDefault("agents.coder.model", models.AzureGPT41)
280+
viper.SetDefault("agents.task.model", models.AzureGPT41Mini)
281+
viper.SetDefault("agents.title.model", models.AzureGPT41Mini)
282+
return
283+
}
275284
}
276285

277286
// hasAWSCredentials checks if AWS credentials are available in the environment.
@@ -506,6 +515,8 @@ func getProviderAPIKey(provider models.ModelProvider) string {
506515
return os.Getenv("GEMINI_API_KEY")
507516
case models.ProviderGROQ:
508517
return os.Getenv("GROQ_API_KEY")
518+
case models.ProviderAzure:
519+
return os.Getenv("AZURE_OPENAI_API_KEY")
509520
case models.ProviderBedrock:
510521
if hasAWSCredentials() {
511522
return "aws-credentials-available"

internal/llm/models/azure.go

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package models
2+
3+
const ProviderAzure ModelProvider = "azure"
4+
5+
const (
6+
AzureGPT41 ModelID = "azure.gpt-4.1"
7+
AzureGPT41Mini ModelID = "azure.gpt-4.1-mini"
8+
AzureGPT41Nano ModelID = "azure.gpt-4.1-nano"
9+
AzureGPT45Preview ModelID = "azure.gpt-4.5-preview"
10+
AzureGPT4o ModelID = "azure.gpt-4o"
11+
AzureGPT4oMini ModelID = "azure.gpt-4o-mini"
12+
AzureO1 ModelID = "azure.o1"
13+
AzureO1Mini ModelID = "azure.o1-mini"
14+
AzureO3 ModelID = "azure.o3"
15+
AzureO3Mini ModelID = "azure.o3-mini"
16+
AzureO4Mini ModelID = "azure.o4-mini"
17+
)
18+
19+
var AzureModels = map[ModelID]Model{
20+
AzureGPT41: {
21+
ID: AzureGPT41,
22+
Name: "Azure OpenAI – GPT 4.1",
23+
Provider: ProviderAzure,
24+
APIModel: "gpt-4.1",
25+
CostPer1MIn: OpenAIModels[GPT41].CostPer1MIn,
26+
CostPer1MInCached: OpenAIModels[GPT41].CostPer1MInCached,
27+
CostPer1MOut: OpenAIModels[GPT41].CostPer1MOut,
28+
CostPer1MOutCached: OpenAIModels[GPT41].CostPer1MOutCached,
29+
ContextWindow: OpenAIModels[GPT41].ContextWindow,
30+
DefaultMaxTokens: OpenAIModels[GPT41].DefaultMaxTokens,
31+
},
32+
AzureGPT41Mini: {
33+
ID: AzureGPT41Mini,
34+
Name: "Azure OpenAI – GPT 4.1 mini",
35+
Provider: ProviderAzure,
36+
APIModel: "gpt-4.1-mini",
37+
CostPer1MIn: OpenAIModels[GPT41Mini].CostPer1MIn,
38+
CostPer1MInCached: OpenAIModels[GPT41Mini].CostPer1MInCached,
39+
CostPer1MOut: OpenAIModels[GPT41Mini].CostPer1MOut,
40+
CostPer1MOutCached: OpenAIModels[GPT41Mini].CostPer1MOutCached,
41+
ContextWindow: OpenAIModels[GPT41Mini].ContextWindow,
42+
DefaultMaxTokens: OpenAIModels[GPT41Mini].DefaultMaxTokens,
43+
},
44+
AzureGPT41Nano: {
45+
ID: AzureGPT41Nano,
46+
Name: "Azure OpenAI – GPT 4.1 nano",
47+
Provider: ProviderAzure,
48+
APIModel: "gpt-4.1-nano",
49+
CostPer1MIn: OpenAIModels[GPT41Nano].CostPer1MIn,
50+
CostPer1MInCached: OpenAIModels[GPT41Nano].CostPer1MInCached,
51+
CostPer1MOut: OpenAIModels[GPT41Nano].CostPer1MOut,
52+
CostPer1MOutCached: OpenAIModels[GPT41Nano].CostPer1MOutCached,
53+
ContextWindow: OpenAIModels[GPT41Nano].ContextWindow,
54+
DefaultMaxTokens: OpenAIModels[GPT41Nano].DefaultMaxTokens,
55+
},
56+
AzureGPT45Preview: {
57+
ID: AzureGPT45Preview,
58+
Name: "Azure OpenAI – GPT 4.5 preview",
59+
Provider: ProviderAzure,
60+
APIModel: "gpt-4.5-preview",
61+
CostPer1MIn: OpenAIModels[GPT45Preview].CostPer1MIn,
62+
CostPer1MInCached: OpenAIModels[GPT45Preview].CostPer1MInCached,
63+
CostPer1MOut: OpenAIModels[GPT45Preview].CostPer1MOut,
64+
CostPer1MOutCached: OpenAIModels[GPT45Preview].CostPer1MOutCached,
65+
ContextWindow: OpenAIModels[GPT45Preview].ContextWindow,
66+
DefaultMaxTokens: OpenAIModels[GPT45Preview].DefaultMaxTokens,
67+
},
68+
AzureGPT4o: {
69+
ID: AzureGPT4o,
70+
Name: "Azure OpenAI – GPT-4o",
71+
Provider: ProviderAzure,
72+
APIModel: "gpt-4o",
73+
CostPer1MIn: OpenAIModels[GPT4o].CostPer1MIn,
74+
CostPer1MInCached: OpenAIModels[GPT4o].CostPer1MInCached,
75+
CostPer1MOut: OpenAIModels[GPT4o].CostPer1MOut,
76+
CostPer1MOutCached: OpenAIModels[GPT4o].CostPer1MOutCached,
77+
ContextWindow: OpenAIModels[GPT4o].ContextWindow,
78+
DefaultMaxTokens: OpenAIModels[GPT4o].DefaultMaxTokens,
79+
},
80+
AzureGPT4oMini: {
81+
ID: AzureGPT4oMini,
82+
Name: "Azure OpenAI – GPT-4o mini",
83+
Provider: ProviderAzure,
84+
APIModel: "gpt-4o-mini",
85+
CostPer1MIn: OpenAIModels[GPT4oMini].CostPer1MIn,
86+
CostPer1MInCached: OpenAIModels[GPT4oMini].CostPer1MInCached,
87+
CostPer1MOut: OpenAIModels[GPT4oMini].CostPer1MOut,
88+
CostPer1MOutCached: OpenAIModels[GPT4oMini].CostPer1MOutCached,
89+
ContextWindow: OpenAIModels[GPT4oMini].ContextWindow,
90+
DefaultMaxTokens: OpenAIModels[GPT4oMini].DefaultMaxTokens,
91+
},
92+
AzureO1: {
93+
ID: AzureO1,
94+
Name: "Azure OpenAI – O1",
95+
Provider: ProviderAzure,
96+
APIModel: "o1",
97+
CostPer1MIn: OpenAIModels[O1].CostPer1MIn,
98+
CostPer1MInCached: OpenAIModels[O1].CostPer1MInCached,
99+
CostPer1MOut: OpenAIModels[O1].CostPer1MOut,
100+
CostPer1MOutCached: OpenAIModels[O1].CostPer1MOutCached,
101+
ContextWindow: OpenAIModels[O1].ContextWindow,
102+
DefaultMaxTokens: OpenAIModels[O1].DefaultMaxTokens,
103+
CanReason: OpenAIModels[O1].CanReason,
104+
},
105+
AzureO1Mini: {
106+
ID: AzureO1Mini,
107+
Name: "Azure OpenAI – O1 mini",
108+
Provider: ProviderAzure,
109+
APIModel: "o1-mini",
110+
CostPer1MIn: OpenAIModels[O1Mini].CostPer1MIn,
111+
CostPer1MInCached: OpenAIModels[O1Mini].CostPer1MInCached,
112+
CostPer1MOut: OpenAIModels[O1Mini].CostPer1MOut,
113+
CostPer1MOutCached: OpenAIModels[O1Mini].CostPer1MOutCached,
114+
ContextWindow: OpenAIModels[O1Mini].ContextWindow,
115+
DefaultMaxTokens: OpenAIModels[O1Mini].DefaultMaxTokens,
116+
CanReason: OpenAIModels[O1Mini].CanReason,
117+
},
118+
AzureO3: {
119+
ID: AzureO3,
120+
Name: "Azure OpenAI – O3",
121+
Provider: ProviderAzure,
122+
APIModel: "o3",
123+
CostPer1MIn: OpenAIModels[O3].CostPer1MIn,
124+
CostPer1MInCached: OpenAIModels[O3].CostPer1MInCached,
125+
CostPer1MOut: OpenAIModels[O3].CostPer1MOut,
126+
CostPer1MOutCached: OpenAIModels[O3].CostPer1MOutCached,
127+
ContextWindow: OpenAIModels[O3].ContextWindow,
128+
DefaultMaxTokens: OpenAIModels[O3].DefaultMaxTokens,
129+
CanReason: OpenAIModels[O3].CanReason,
130+
},
131+
AzureO3Mini: {
132+
ID: AzureO3Mini,
133+
Name: "Azure OpenAI – O3 mini",
134+
Provider: ProviderAzure,
135+
APIModel: "o3-mini",
136+
CostPer1MIn: OpenAIModels[O3Mini].CostPer1MIn,
137+
CostPer1MInCached: OpenAIModels[O3Mini].CostPer1MInCached,
138+
CostPer1MOut: OpenAIModels[O3Mini].CostPer1MOut,
139+
CostPer1MOutCached: OpenAIModels[O3Mini].CostPer1MOutCached,
140+
ContextWindow: OpenAIModels[O3Mini].ContextWindow,
141+
DefaultMaxTokens: OpenAIModels[O3Mini].DefaultMaxTokens,
142+
CanReason: OpenAIModels[O3Mini].CanReason,
143+
},
144+
AzureO4Mini: {
145+
ID: AzureO4Mini,
146+
Name: "Azure OpenAI – O4 mini",
147+
Provider: ProviderAzure,
148+
APIModel: "o4-mini",
149+
CostPer1MIn: OpenAIModels[O4Mini].CostPer1MIn,
150+
CostPer1MInCached: OpenAIModels[O4Mini].CostPer1MInCached,
151+
CostPer1MOut: OpenAIModels[O4Mini].CostPer1MOut,
152+
CostPer1MOutCached: OpenAIModels[O4Mini].CostPer1MOutCached,
153+
ContextWindow: OpenAIModels[O4Mini].ContextWindow,
154+
DefaultMaxTokens: OpenAIModels[O4Mini].DefaultMaxTokens,
155+
CanReason: OpenAIModels[O4Mini].CanReason,
156+
},
157+
}

internal/llm/models/models.go

+1
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,5 @@ func init() {
7676
maps.Copy(SupportedModels, OpenAIModels)
7777
maps.Copy(SupportedModels, GeminiModels)
7878
maps.Copy(SupportedModels, GroqModels)
79+
maps.Copy(SupportedModels, AzureModels)
7980
}

internal/llm/provider/azure.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package provider
2+
3+
import (
4+
"os"
5+
6+
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
7+
"github.com/openai/openai-go"
8+
"github.com/openai/openai-go/azure"
9+
"github.com/openai/openai-go/option"
10+
)
11+
12+
type azureClient struct {
13+
*openaiClient
14+
}
15+
16+
type AzureClient ProviderClient
17+
18+
func newAzureClient(opts providerClientOptions) AzureClient {
19+
20+
endpoint := os.Getenv("AZURE_OPENAI_ENDPOINT") // ex: https://foo.openai.azure.com
21+
apiVersion := os.Getenv("AZURE_OPENAI_API_VERSION") // ex: 2025-04-01-preview
22+
23+
if endpoint == "" || apiVersion == "" {
24+
return &azureClient{openaiClient: newOpenAIClient(opts).(*openaiClient)}
25+
}
26+
27+
reqOpts := []option.RequestOption{
28+
azure.WithEndpoint(endpoint, apiVersion),
29+
}
30+
31+
if opts.apiKey != "" || os.Getenv("AZURE_OPENAI_API_KEY") != "" {
32+
key := opts.apiKey
33+
if key == "" {
34+
key = os.Getenv("AZURE_OPENAI_API_KEY")
35+
}
36+
reqOpts = append(reqOpts, azure.WithAPIKey(key))
37+
} else if cred, err := azidentity.NewDefaultAzureCredential(nil); err == nil {
38+
reqOpts = append(reqOpts, azure.WithTokenCredential(cred))
39+
}
40+
41+
base := &openaiClient{
42+
providerOptions: opts,
43+
client: openai.NewClient(reqOpts...),
44+
}
45+
46+
return &azureClient{openaiClient: base}
47+
}

0 commit comments

Comments
 (0)