| 41 | var _ Provider = &OpenAI{} |
| 42 | |
| 43 | func NewOpenAI(cfg config.OpenAI) *OpenAI { |
| 44 | if cfg.Name == "" { |
| 45 | cfg.Name = config.ProviderOpenAI |
| 46 | } |
| 47 | if cfg.BaseURL == "" { |
| 48 | cfg.BaseURL = "https://api.openai.com/v1/" |
| 49 | } |
| 50 | // Resolve centralized key configuration into KeyPool. |
| 51 | // Precedence: |
| 52 | // 1. cfg.KeyPool (explicit, highest priority). |
| 53 | // 2. cfg.Key (legacy single key). |
| 54 | // After this block cfg.Key is empty so it can only carry a |
| 55 | // BYOK Authorization Bearer set per interception in |
| 56 | // CreateInterceptor. |
| 57 | // TODO(ssncferreira): simplify auth field resolution per |
| 58 | // https://github.com/coder/aibridge/issues/266. |
| 59 | if cfg.KeyPool == nil && cfg.Key != "" { |
| 60 | // keypool.New only fails on empty or duplicate keys, |
| 61 | // neither possible with a single non-empty key. |
| 62 | pool, err := keypool.New([]string{cfg.Key}, quartz.NewReal()) |
| 63 | if err != nil { |
| 64 | panic(fmt.Sprintf("openai provider: build single-key pool: %s", err)) |
| 65 | } |
| 66 | cfg.KeyPool = pool |
| 67 | } |
| 68 | cfg.Key = "" |
| 69 | if cfg.CircuitBreaker != nil { |
| 70 | cfg.CircuitBreaker.OpenErrorResponse = openAIOpenErrorResponse |
| 71 | } |
| 72 | |
| 73 | return &OpenAI{ |
| 74 | cfg: cfg, |
| 75 | circuitBreaker: cfg.CircuitBreaker, |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | func (*OpenAI) Type() string { |
| 80 | return config.ProviderOpenAI |