1// Package azure provides an implementation of the fantasy AI SDK for Azure's language models.
2package azure
3
4import (
5 "fmt"
6 "regexp"
7 "strings"
8
9 "charm.land/fantasy"
10 "charm.land/fantasy/providers/openai"
11 "github.com/openai/openai-go/v2/azure"
12 "github.com/openai/openai-go/v2/option"
13)
14
15type options struct {
16 baseURL string
17 apiKey string
18 apiVersion string
19
20 openaiOptions []openai.Option
21}
22
23const (
24 // Name is the name of the Azure provider.
25 Name = "azure"
26 // defaultAPIVersion is the default API version for Azure.
27 defaultAPIVersion = "2025-01-01-preview"
28)
29
30// azureURLPattern matches Azure OpenAI endpoint URLs in various formats:
31// * https://resource-id.openai.azure.com;
32// * https://resource-id.openai.azure.com/;
33// * https://resource-id.cognitiveservices.azure.com;
34// * https://resource-id.services.ai.azure.com/api/projects/project-name;
35// * resource-id.openai.azure.com.
36var azureURLPattern = regexp.MustCompile(`^(?:https?://)?([a-zA-Z0-9-]+)\.(?:openai|cognitiveservices|services\.ai)\.azure\.com(?:/.*)?$`)
37
38// Option defines a function that configures Azure provider options.
39type Option = func(*options)
40
41// New creates a new Azure provider with the given options.
42func New(opts ...Option) (fantasy.Provider, error) {
43 o := options{
44 apiVersion: defaultAPIVersion,
45 }
46 for _, opt := range opts {
47 opt(&o)
48 }
49 return openai.New(
50 append(
51 o.openaiOptions,
52 openai.WithName(Name),
53 openai.WithBaseURL(o.baseURL),
54 openai.WithSDKOptions(
55 azure.WithAPIKey(o.apiKey),
56 ),
57 )...,
58 )
59}
60
61// WithBaseURL sets the base URL for the Azure provider.
62func WithBaseURL(baseURL string) Option {
63 return func(o *options) {
64 o.baseURL = parseAzureURL(baseURL)
65 }
66}
67
68// parseAzureURL extracts the resource ID from various Azure URL formats
69// and returns the standardized OpenAI-compatible endpoint URL.
70// If the URL doesn't match known Azure patterns, it returns the original URL.
71func parseAzureURL(baseURL string) string {
72 matches := azureURLPattern.FindStringSubmatch(baseURL)
73 if len(matches) >= 2 {
74 resourceID := matches[1]
75 return fmt.Sprintf("https://%s.openai.azure.com/openai/v1", resourceID)
76 }
77 // fallback to use the provided url
78 if !strings.HasPrefix(baseURL, "http://") && !strings.HasPrefix(baseURL, "https://") {
79 return "https://" + baseURL
80 }
81 return baseURL
82}
83
84// WithAPIKey sets the API key for the Azure provider.
85func WithAPIKey(apiKey string) Option {
86 return func(o *options) {
87 o.apiKey = apiKey
88 }
89}
90
91// WithHeaders sets the headers for the Azure provider.
92func WithHeaders(headers map[string]string) Option {
93 return func(o *options) {
94 o.openaiOptions = append(o.openaiOptions, openai.WithHeaders(headers))
95 }
96}
97
98// WithAPIVersion sets the API version for the Azure provider.
99func WithAPIVersion(version string) Option {
100 return func(o *options) {
101 o.apiVersion = version
102 }
103}
104
105// WithHTTPClient sets the HTTP client for the Azure provider.
106func WithHTTPClient(client option.HTTPClient) Option {
107 return func(o *options) {
108 o.openaiOptions = append(o.openaiOptions, openai.WithHTTPClient(client))
109 }
110}
111
112// WithUseResponsesAPI configures the provider to use the responses API for models that support it.
113func WithUseResponsesAPI() Option {
114 return func(o *options) {
115 o.openaiOptions = append(o.openaiOptions, openai.WithUseResponsesAPI())
116 }
117}