-
Notifications
You must be signed in to change notification settings - Fork 15.1k
fix: 修复 dist 构建产物运行时 API Error 系列问题 #269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import OpenAI from 'openai' | ||
| import type OpenAI from 'openai' | ||
| import { getProxyFetchOptions } from 'src/utils/proxy.js' | ||
|
|
||
| /** | ||
|
|
@@ -12,17 +12,18 @@ const DEFAULT_BASE_URL = 'https://api.x.ai/v1' | |
|
|
||
| let cachedClient: OpenAI | null = null | ||
|
|
||
| export function getGrokClient(options?: { | ||
| export async function getGrokClient(options?: { | ||
| maxRetries?: number | ||
| fetchOverride?: typeof fetch | ||
| source?: string | ||
| }): OpenAI { | ||
| }): Promise<OpenAI> { | ||
|
Comment on lines
+15
to
+19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Description: Find all usages of getGrokClient to verify they're awaited
echo "=== All getGrokClient calls in test files ==="
rg -n "getGrokClient\(\)" --type ts -g '*test*' -g '*spec*'
echo "=== All getGrokClient calls in source files ==="
rg -n "getGrokClient\(" --type ts -g '!*test*' -g '!*spec*'Repository: claude-code-best/claude-code Length of output: 837 🏁 Script executed: cat -n src/services/api/grok/__tests__/client.test.ts | head -60Repository: claude-code-best/claude-code Length of output: 1659 🏁 Script executed: cat -n src/services/api/grok/client.ts | head -40Repository: claude-code-best/claude-code Length of output: 1526 Tests need to be updated to handle the async signature. The async conversion of Update the tests to be async functions and await all 🤖 Prompt for AI Agents |
||
| if (cachedClient) return cachedClient | ||
|
|
||
| const { default: OpenAIClass } = await import('openai') | ||
| const apiKey = process.env.GROK_API_KEY || process.env.XAI_API_KEY || '' | ||
| const baseURL = process.env.GROK_BASE_URL || DEFAULT_BASE_URL | ||
|
|
||
| const client = new OpenAI({ | ||
| const client = new OpenAIClass({ | ||
| apiKey, | ||
| baseURL, | ||
| maxRetries: options?.maxRetries ?? 0, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import OpenAI from 'openai' | ||
| import type OpenAI from 'openai' | ||
| import { getProxyFetchOptions } from 'src/utils/proxy.js' | ||
| import { isEnvTruthy } from 'src/utils/envUtils.js' | ||
|
|
||
|
|
@@ -13,17 +13,18 @@ import { isEnvTruthy } from 'src/utils/envUtils.js' | |
|
|
||
| let cachedClient: OpenAI | null = null | ||
|
|
||
| export function getOpenAIClient(options?: { | ||
| export async function getOpenAIClient(options?: { | ||
| maxRetries?: number | ||
| fetchOverride?: typeof fetch | ||
| source?: string | ||
| }): OpenAI { | ||
| }): Promise<OpenAI> { | ||
| if (cachedClient) return cachedClient | ||
|
|
||
| const { default: OpenAIClass } = await import('openai') | ||
|
Comment on lines
+16
to
+23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify current guard state and call-path usage.
rg -nP --type=ts -C3 '\bcachedClient\b|\bcachedClientPromise\b|getOpenAIClient\s*\(' \
src/services/api/openai/client.ts src/services/api/openai/index.tsRepository: claude-code-best/claude-code Length of output: 2165 🏁 Script executed: #!/bin/bash
# Get the full getOpenAIClient function and surrounding context
cat -n src/services/api/openai/client.ts | head -50Repository: claude-code-best/claude-code Length of output: 1994 🏁 Script executed: #!/bin/bash
# Check if there are any tests for the client
find . -type f -name "*test*" -o -name "*spec*" | grep -i "openai.*client" | head -20Repository: claude-code-best/claude-code Length of output: 54 🏁 Script executed: #!/bin/bash
# Check how getOpenAIClient is used across the codebase
rg -l "getOpenAIClient" --type=tsRepository: claude-code-best/claude-code Length of output: 197 🏁 Script executed: #!/bin/bash
# Check the test file for concurrent/race condition tests
cat -n src/services/api/openai/__tests__/queryModelOpenAI.test.ts | head -100Repository: claude-code-best/claude-code Length of output: 4866 🏁 Script executed: #!/bin/bash
# Check context around line 293 in index.ts to understand fetchOverride usage
sed -n '280,310p' src/services/api/openai/index.tsRepository: claude-code-best/claude-code Length of output: 1422 🏁 Script executed: #!/bin/bash
# Check if there are other concurrent calls to getOpenAIClient
rg -B2 -A2 "getOpenAIClient" src/services/api/openai/index.ts | head -50Repository: claude-code-best/claude-code Length of output: 450 🏁 Script executed: #!/bin/bash
# Verify the Promise type is correctly used and check for any TypeScript compatibility
rg -A5 "Promise\<OpenAI\>" src/services/api/openai/client.tsRepository: claude-code-best/claude-code Length of output: 162 🏁 Script executed: #!/bin/bash
# Check if there are any concurrency tests or Promise-based tests in the test file
grep -n "Promise\|concurrent\|race\|parallel" src/services/api/openai/__tests__/queryModelOpenAI.test.tsRepository: claude-code-best/claude-code Length of output: 54 🏁 Script executed: #!/bin/bash
# Check for any Promise types in the file
grep -n "Promise" src/services/api/openai/client.tsRepository: claude-code-best/claude-code Length of output: 96 🏁 Script executed: #!/bin/bash
# Verify the fix would work by checking the buildClient arrow function pattern is valid TypeScript
# Look for similar async arrow function patterns in the codebase
rg "const .+ = async \(\)" --type=ts | head -5Repository: claude-code-best/claude-code Length of output: 645 Add in-flight promise guard to prevent duplicate client construction during concurrent async initialization. The current implementation checks Implement in-flight promise deduplication: track a 🔧 Suggested fix let cachedClient: OpenAI | null = null
+let cachedClientPromise: Promise<OpenAI> | null = null
export async function getOpenAIClient(options?: {
maxRetries?: number
fetchOverride?: typeof fetch
source?: string
}): Promise<OpenAI> {
- if (cachedClient) return cachedClient
-
- const { default: OpenAIClass } = await import('openai')
- const apiKey = process.env.OPENAI_API_KEY || ''
- const baseURL = process.env.OPENAI_BASE_URL
-
- const client = new OpenAIClass({
- apiKey,
- ...(baseURL && { baseURL }),
- maxRetries: options?.maxRetries ?? 0,
- timeout: parseInt(process.env.API_TIMEOUT_MS || String(600 * 1000), 10),
- dangerouslyAllowBrowser: true,
- ...(process.env.OPENAI_ORG_ID && { organization: process.env.OPENAI_ORG_ID }),
- ...(process.env.OPENAI_PROJECT_ID && { project: process.env.OPENAI_PROJECT_ID }),
- fetchOptions: getProxyFetchOptions({ forAnthropicAPI: false }),
- ...(options?.fetchOverride && { fetch: options.fetchOverride }),
- })
-
- if (!options?.fetchOverride) {
- cachedClient = client
- }
-
- return client
+ if (!options?.fetchOverride) {
+ if (cachedClient) return cachedClient
+ if (cachedClientPromise) return cachedClientPromise
+ }
+
+ const buildClient = async (): Promise<OpenAI> => {
+ const { default: OpenAIClass } = await import('openai')
+ const apiKey = process.env.OPENAI_API_KEY || ''
+ const baseURL = process.env.OPENAI_BASE_URL
+
+ return new OpenAIClass({
+ apiKey,
+ ...(baseURL && { baseURL }),
+ maxRetries: options?.maxRetries ?? 0,
+ timeout: parseInt(process.env.API_TIMEOUT_MS || String(600 * 1000), 10),
+ dangerouslyAllowBrowser: true,
+ ...(process.env.OPENAI_ORG_ID && { organization: process.env.OPENAI_ORG_ID }),
+ ...(process.env.OPENAI_PROJECT_ID && { project: process.env.OPENAI_PROJECT_ID }),
+ fetchOptions: getProxyFetchOptions({ forAnthropicAPI: false }),
+ ...(options?.fetchOverride && { fetch: options.fetchOverride }),
+ })
+ }
+
+ if (options?.fetchOverride) return buildClient()
+
+ cachedClientPromise = buildClient()
+ try {
+ cachedClient = await cachedClientPromise
+ return cachedClient
+ } finally {
+ cachedClientPromise = null
+ }
}🤖 Prompt for AI Agents |
||
| const apiKey = process.env.OPENAI_API_KEY || '' | ||
| const baseURL = process.env.OPENAI_BASE_URL | ||
|
|
||
| const client = new OpenAI({ | ||
| const client = new OpenAIClass({ | ||
| apiKey, | ||
| ...(baseURL && { baseURL }), | ||
| maxRetries: options?.maxRetries ?? 0, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 265
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 506
🏁 Script executed:
Repository: claude-code-best/claude-code
Length of output: 26206
Move
openaifromdevDependenciestodependencies.Marking
openaias external correctly prevents Bun from bundling it, which resolves the private class field initialization issue. However,openaiis currently indevDependenciesand is required at runtime by the bundled CLI.When users install this package via npm,
openaiwill not be installed (devDependencies are excluded), causing runtime errors when they use the OpenAI or Grok provider:src/services/api/openai/client.tsdynamically imports openaisrc/services/api/grok/client.tsuses openai for the Grok providerMove
openaitodependenciesto ensure it's installed alongside the CLI.🤖 Prompt for AI Agents