@@ -2,7 +2,6 @@ import OpenAI from 'openai'
22import { openaiAdapter } from 'src/services/providerUsage/adapters/openai.js'
33import { updateProviderBuckets } from 'src/services/providerUsage/store.js'
44import { getProxyFetchOptions } from 'src/utils/proxy.js'
5- import { isEnvTruthy } from 'src/utils/envUtils.js'
65
76/**
87 * Environment variables:
@@ -15,6 +14,22 @@ import { isEnvTruthy } from 'src/utils/envUtils.js'
1514
1615let cachedClient : OpenAI | null = null
1716
17+ const DEFAULT_COMPATIBLE_USER_AGENT = 'opencode/1.14.22 (win32 10.0.26200; x64) ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.13'
18+
19+ /**
20+ * Some OpenAI-compatible gateways sit behind Cloudflare/sub2api rules that
21+ * block SDK-looking user agents such as `OpenAI/JS ...`. Keep the official
22+ * OpenAI path unchanged, but make custom base URLs look like a generic client.
23+ */
24+ export function getOpenAICompatibleDefaultHeaders ( baseURL ?: string ) : Record < string , string > | undefined {
25+ const userAgent = process . env . OPENAI_USER_AGENT || ( baseURL ? DEFAULT_COMPATIBLE_USER_AGENT : undefined )
26+ if ( ! userAgent ) return undefined
27+
28+ return {
29+ 'User-Agent' : userAgent ,
30+ }
31+ }
32+
1833/**
1934 * Wrap a fetch so that every response's rate-limit headers are fed into the
2035 * provider usage store. Errors in parsing must never break the request.
@@ -49,10 +64,12 @@ export function getOpenAIClient(options?: {
4964
5065 const baseFetch = options ?. fetchOverride ?? ( globalThis . fetch as typeof fetch )
5166 const wrappedFetch = wrapFetchForUsage ( baseFetch )
67+ const defaultHeaders = getOpenAICompatibleDefaultHeaders ( baseURL )
5268
5369 const client = new OpenAI ( {
5470 apiKey,
5571 ...( baseURL && { baseURL } ) ,
72+ ...( defaultHeaders && { defaultHeaders } ) ,
5673 maxRetries : options ?. maxRetries ?? 0 ,
5774 timeout : parseInt ( process . env . API_TIMEOUT_MS || String ( 600 * 1000 ) , 10 ) ,
5875 dangerouslyAllowBrowser : true ,
0 commit comments