Skip to content

Commit 0e0ffca

Browse files
committed
fix: 调整 OpenAI 兼容请求 User-Agent
1 parent ecb5339 commit 0e0ffca

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

src/services/api/openai/__tests__/thinking.test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,41 @@
11
import { describe, expect, test, beforeEach, afterEach } from 'bun:test'
22
import { isOpenAIThinkingEnabled, buildOpenAIRequestBody } from '../requestBody.js'
3+
import { getOpenAICompatibleDefaultHeaders } from '../client.js'
4+
5+
describe('getOpenAICompatibleDefaultHeaders', () => {
6+
const originalEnv = {
7+
OPENAI_USER_AGENT: process.env.OPENAI_USER_AGENT,
8+
}
9+
10+
beforeEach(() => {
11+
delete process.env.OPENAI_USER_AGENT
12+
})
13+
14+
afterEach(() => {
15+
if (originalEnv.OPENAI_USER_AGENT === undefined) {
16+
delete process.env.OPENAI_USER_AGENT
17+
} else {
18+
process.env.OPENAI_USER_AGENT = originalEnv.OPENAI_USER_AGENT
19+
}
20+
})
21+
22+
test('does not override the official OpenAI default path', () => {
23+
expect(getOpenAICompatibleDefaultHeaders()).toBeUndefined()
24+
})
25+
26+
test('uses a generic user agent for OpenAI-compatible base URLs', () => {
27+
expect(getOpenAICompatibleDefaultHeaders('https://sub2api.example/v1')).toEqual({
28+
'User-Agent': 'opencode/1.14.22 (win32 10.0.26200; x64) ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.13',
29+
})
30+
})
31+
32+
test('allows explicit user agent override', () => {
33+
process.env.OPENAI_USER_AGENT = 'Mozilla/5.0 test-client'
34+
expect(getOpenAICompatibleDefaultHeaders('https://sub2api.example/v1')).toEqual({
35+
'User-Agent': 'Mozilla/5.0 test-client',
36+
})
37+
})
38+
})
339

440
describe('isOpenAIThinkingEnabled', () => {
541
const originalEnv = {
@@ -236,4 +272,4 @@ describe('buildOpenAIRequestBody — thinking params', () => {
236272
expect(body.tools).toBeUndefined()
237273
expect(body.tool_choice).toBeUndefined()
238274
})
239-
})
275+
})

src/services/api/openai/client.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import OpenAI from 'openai'
22
import { openaiAdapter } from 'src/services/providerUsage/adapters/openai.js'
33
import { updateProviderBuckets } from 'src/services/providerUsage/store.js'
44
import { 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

1615
let 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

Comments
 (0)