on main help sign in
feat(agent): add support for azure provider
cb946b7kujtimiihoxha
go.mod+6−2
@@ -15,7 +15,7 @@ require (
15 github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250910155747-997384b0b35e
15 github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250910155747-997384b0b35e
16 github.com/charmbracelet/catwalk v0.6.5-0.20251010111259-a1bafd2530bb
16 github.com/charmbracelet/catwalk v0.6.5-0.20251010111259-a1bafd2530bb
17 github.com/charmbracelet/fang v0.4.2
17 github.com/charmbracelet/fang v0.4.2
18− github.com/charmbracelet/fantasy v0.0.0-20251003071236-5d39f0348e5d
18+ github.com/charmbracelet/fantasy v0.0.0-20251010114724-801d5df11cef
19 github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018
19 github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018
20 github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea
20 github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea
21 github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706
21 github.com/charmbracelet/log/v2 v2.0.0-20250226163916-c379e29ff706
@@ -47,6 +47,11 @@ require (
47 mvdan.cc/sh/v3 v3.12.1-0.20250902163504-3cf4fd5717a5
47 mvdan.cc/sh/v3 v3.12.1-0.20250902163504-3cf4fd5717a5
48)
48)
49
49
50+require (
51+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
52+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
53+)
54+
50require (
55require (
51 cloud.google.com/go v0.116.0 // indirect
56 cloud.google.com/go v0.116.0 // indirect
52 cloud.google.com/go/auth v0.9.3 // indirect
57 cloud.google.com/go/auth v0.9.3 // indirect
@@ -133,7 +138,6 @@ require (
133 google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
138 google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
134 google.golang.org/grpc v1.66.2 // indirect
139 google.golang.org/grpc v1.66.2 // indirect
135 google.golang.org/protobuf v1.36.8 // indirect
140 google.golang.org/protobuf v1.36.8 // indirect
136− gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
137 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
141 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
138 gopkg.in/yaml.v3 v3.0.1 // indirect
142 gopkg.in/yaml.v3 v3.0.1 // indirect
139 mvdan.cc/sh/moreinterp v0.0.0-20250902163504-3cf4fd5717a5
143 mvdan.cc/sh/moreinterp v0.0.0-20250902163504-3cf4fd5717a5
go.sum+14−3
@@ -5,6 +5,14 @@ cloud.google.com/go/auth v0.9.3 h1:VOEUIAADkkLtyfr3BLa3R8Ed/j6w1jTBmARx+wb5w5U=
5cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
5cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
6cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
6cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
7cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
7cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
8+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
9+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
10+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
11+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
12+github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
13+github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
14+github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
15+github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
8github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
16github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
9github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5x+rHJnb1ssNmqpLH/k=
17github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5x+rHJnb1ssNmqpLH/k=
10github.com/JohannesKaufmann/html-to-markdown v1.6.0/go.mod h1:NUI78lGg/a7vpEJTz/0uOcYMaibytE4BUOQS8k78yPQ=
18github.com/JohannesKaufmann/html-to-markdown v1.6.0/go.mod h1:NUI78lGg/a7vpEJTz/0uOcYMaibytE4BUOQS8k78yPQ=
@@ -51,8 +59,8 @@ github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqI
51github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
59github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
52github.com/charmbracelet/fang v0.4.2 h1:nWr7Tb82/TTNNGMGG35aTZ1X68loAOQmpb0qxkKXjas=
60github.com/charmbracelet/fang v0.4.2 h1:nWr7Tb82/TTNNGMGG35aTZ1X68loAOQmpb0qxkKXjas=
53github.com/charmbracelet/fang v0.4.2/go.mod h1:wHJKQYO5ReYsxx+yZl+skDtrlKO/4LLEQ6EXsdHhRhg=
61github.com/charmbracelet/fang v0.4.2/go.mod h1:wHJKQYO5ReYsxx+yZl+skDtrlKO/4LLEQ6EXsdHhRhg=
54−github.com/charmbracelet/fantasy v0.0.0-20251003071236-5d39f0348e5d h1:HS9qu7CgQGPnPcNoHIZXGWGFS8SaxaBXYytLdakm+jY=
62+github.com/charmbracelet/fantasy v0.0.0-20251010114724-801d5df11cef h1:4GmaYNj/NifjXbW6K3QExrCuiRpoNMWAlJESzmppdGU=
55−github.com/charmbracelet/fantasy v0.0.0-20251003071236-5d39f0348e5d/go.mod h1:RZotHpq44tKZDe6Vf0kk1iDqnUgH7Scx+K/7uJ9Qwnw=
63+github.com/charmbracelet/fantasy v0.0.0-20251010114724-801d5df11cef/go.mod h1:3XBTxXYYvBX1UsKa5FHicJUQJkoHHmUjXRfr0BcqV4A=
56github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018 h1:PU4Zvpagsk5sgaDxn5W4sxHuLp9QRMBZB3bFSk40A4w=
64github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018 h1:PU4Zvpagsk5sgaDxn5W4sxHuLp9QRMBZB3bFSk40A4w=
57github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018/go.mod h1:Z/GLmp9fzaqX4ze3nXG7StgWez5uBM5XtlLHK8V/qSk=
65github.com/charmbracelet/glamour/v2 v2.0.0-20250811143442-a27abb32f018/go.mod h1:Z/GLmp9fzaqX4ze3nXG7StgWez5uBM5XtlLHK8V/qSk=
58github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea h1:g1HfUgSMvye8mgecMD1mPscpt+pzJoDEiSA+p2QXzdQ=
66github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250917201909-41ff0bf215ea h1:g1HfUgSMvye8mgecMD1mPscpt+pzJoDEiSA+p2QXzdQ=
@@ -114,6 +122,8 @@ github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7
114github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
122github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
115github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
123github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
116github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
124github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
125+github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
126+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
117github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
127github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
118github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
128github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
119github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
129github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
@@ -167,7 +177,6 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
167github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
177github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
168github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
178github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
169github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
179github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
170−github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
171github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
180github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
172github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
181github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
173github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
182github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -218,6 +227,8 @@ github.com/openai/openai-go/v2 v2.3.0 h1:y9U+V1tlHjvvb/5XIswuySqnG5EnKBFAbMxgBvT
218github.com/openai/openai-go/v2 v2.3.0/go.mod h1:sIUkR+Cu/PMUVkSKhkk742PRURkQOCFhiwJ7eRSBqmk=
227github.com/openai/openai-go/v2 v2.3.0/go.mod h1:sIUkR+Cu/PMUVkSKhkk742PRURkQOCFhiwJ7eRSBqmk=
219github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
228github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
220github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
229github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
230+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
231+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
221github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
232github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
222github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
233github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
223github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
234github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
internal/agent/common_test.go+1−1
@@ -79,7 +79,7 @@ func openRouterBuilder(model string) builderFunc {
79func zAIBuilder(model string) builderFunc {
79func zAIBuilder(model string) builderFunc {
80 return func(t *testing.T, r *recorder.Recorder) (ai.LanguageModel, error) {
80 return func(t *testing.T, r *recorder.Recorder) (ai.LanguageModel, error) {
81 provider := openaicompat.New(
81 provider := openaicompat.New(
82− "https://api.z.ai/api/coding/paas/v4",
82+ openaicompat.WithBaseURL("https://api.z.ai/api/coding/paas/v4"),
83 openaicompat.WithAPIKey(os.Getenv("CRUSH_ZAI_API_KEY")),
83 openaicompat.WithAPIKey(os.Getenv("CRUSH_ZAI_API_KEY")),
84 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
84 openaicompat.WithHTTPClient(&http.Client{Transport: r}),
85 )
85 )
internal/agent/coordinator.go+27−1
@@ -19,6 +19,7 @@ import (
19 "github.com/charmbracelet/crush/internal/session"
19 "github.com/charmbracelet/crush/internal/session"
20 "github.com/charmbracelet/fantasy/ai"
20 "github.com/charmbracelet/fantasy/ai"
21 "github.com/charmbracelet/fantasy/anthropic"
21 "github.com/charmbracelet/fantasy/anthropic"
22+ "github.com/charmbracelet/fantasy/azure"
22 "github.com/charmbracelet/fantasy/google"
23 "github.com/charmbracelet/fantasy/google"
23 "github.com/charmbracelet/fantasy/openai"
24 "github.com/charmbracelet/fantasy/openai"
24 "github.com/charmbracelet/fantasy/openaicompat"
25 "github.com/charmbracelet/fantasy/openaicompat"
@@ -369,6 +370,7 @@ func (c *coordinator) buildOpenrouterProvider(_, apiKey string, headers map[stri
369
370
370func (c *coordinator) buildOpenaiCompatProvider(baseURL, apiKey string, headers map[string]string) ai.Provider {
371func (c *coordinator) buildOpenaiCompatProvider(baseURL, apiKey string, headers map[string]string) ai.Provider {
371 opts := []openaicompat.Option{
372 opts := []openaicompat.Option{
373+ openaicompat.WithBaseURL(baseURL),
372 openaicompat.WithAPIKey(apiKey),
374 openaicompat.WithAPIKey(apiKey),
373 }
375 }
374 if c.cfg.Options.Debug {
376 if c.cfg.Options.Debug {
@@ -379,7 +381,29 @@ func (c *coordinator) buildOpenaiCompatProvider(baseURL, apiKey string, headers
379 opts = append(opts, openaicompat.WithHeaders(headers))
381 opts = append(opts, openaicompat.WithHeaders(headers))
380 }
382 }
381
383
382− return openaicompat.New(baseURL, opts...)
384+ return openaicompat.New(opts...)
385+}
386+
387+func (c *coordinator) buildAzureProvider(baseURL, apiKey string, headers map[string]string, options map[string]string) ai.Provider {
388+ opts := []azure.Option{
389+ azure.WithBaseURL(baseURL),
390+ azure.WithAPIKey(apiKey),
391+ }
392+ if c.cfg.Options.Debug {
393+ httpClient := log.NewHTTPClient()
394+ opts = append(opts, azure.WithHTTPClient(httpClient))
395+ }
396+ if options == nil {
397+ options = make(map[string]string)
398+ }
399+ if apiVersion, ok := options["apiVersion"]; ok {
400+ opts = append(opts, azure.WithAPIVersion(apiVersion))
401+ }
402+ if len(headers) > 0 {
403+ opts = append(opts, azure.WithHeaders(headers))
404+ }
405+
406+ return azure.New(opts...)
383}
407}
384
408
385// TODO: add baseURL for google
409// TODO: add baseURL for google
@@ -435,6 +459,8 @@ func (c *coordinator) buildProvider(providerCfg config.ProviderConfig, model con
435 provider = c.buildAnthropicProvider(baseURL, apiKey, headers)
459 provider = c.buildAnthropicProvider(baseURL, apiKey, headers)
436 case openrouter.Name:
460 case openrouter.Name:
437 provider = c.buildOpenrouterProvider(baseURL, apiKey, headers)
461 provider = c.buildOpenrouterProvider(baseURL, apiKey, headers)
462+ case azure.Name:
463+ provider = c.buildAzureProvider(baseURL, apiKey, headers, providerCfg.ExtraParams)
438 case google.Name:
464 case google.Name:
439 provider = c.buildGoogleProvider(baseURL, apiKey, headers)
465 provider = c.buildGoogleProvider(baseURL, apiKey, headers)
440 case openaicompat.Name:
466 case openaicompat.Name: