mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
fix: Make General proxy timeout authoritative
Non-stream OpenAI-compatible proxy requests still kept the previous 300s timeout floor after the unified network settings merge. That made lower General AI timeout settings ineffective for normal proxy traffic, even though the setting is meant to be the final provider request timeout. Constraint: Unified network settings must remain the final timeout and proxy override for provider protocol paths. Rejected: Keep the 300s non-stream floor | it makes low timeout settings ineffective for normal proxy requests. Confidence: high Scope-risk: narrow Directive: Do not reintroduce per-protocol timeout floors without updating the General network settings contract and tests. Tested: bun test src/server/__tests__/proxy-network-settings.test.ts Tested: bun test src/server/__tests__/conversation-service.test.ts src/server/__tests__/network-settings.test.ts src/server/__tests__/providers.test.ts src/server/__tests__/proxy-network-settings.test.ts Tested: bun run check:coverage Tested: bun run verify Not-tested: Live provider request against a real upstream proxy.
This commit is contained in:
parent
88ac83d42f
commit
4343b3f039
@ -30,6 +30,156 @@ describe('proxy network settings', () => {
|
||||
beforeEach(setup)
|
||||
afterEach(teardown)
|
||||
|
||||
test('uses configured AI request timeout for non-stream upstream requests', async () => {
|
||||
await fs.writeFile(
|
||||
path.join(tmpDir, 'settings.json'),
|
||||
JSON.stringify({
|
||||
network: {
|
||||
aiRequestTimeoutMs: 45_000,
|
||||
proxy: { mode: 'system', url: '' },
|
||||
},
|
||||
}),
|
||||
'utf-8',
|
||||
)
|
||||
|
||||
const svc = new ProviderService()
|
||||
const provider = await svc.addProvider({
|
||||
presetId: 'custom',
|
||||
name: 'OpenAI Proxy',
|
||||
baseUrl: 'https://api.example.com',
|
||||
apiKey: 'sk-test',
|
||||
apiFormat: 'openai_chat',
|
||||
models: {
|
||||
main: 'model-main',
|
||||
haiku: 'model-main',
|
||||
sonnet: 'model-main',
|
||||
opus: 'model-main',
|
||||
},
|
||||
})
|
||||
|
||||
const originalFetch = globalThis.fetch
|
||||
const originalTimeout = AbortSignal.timeout
|
||||
const timeoutCalls: number[] = []
|
||||
globalThis.fetch = mock(async (_url: string | URL | Request, _init?: RequestInit) => {
|
||||
return new Response(JSON.stringify({
|
||||
id: 'chatcmpl-network-timeout',
|
||||
object: 'chat.completion',
|
||||
created: 0,
|
||||
model: 'model-main',
|
||||
choices: [{
|
||||
index: 0,
|
||||
message: { role: 'assistant', content: 'ok' },
|
||||
finish_reason: 'stop',
|
||||
}],
|
||||
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
}) as typeof fetch
|
||||
AbortSignal.timeout = ((ms: number) => {
|
||||
timeoutCalls.push(ms)
|
||||
return originalTimeout(ms)
|
||||
}) as typeof AbortSignal.timeout
|
||||
|
||||
try {
|
||||
const body = {
|
||||
model: 'model-main',
|
||||
max_tokens: 64,
|
||||
messages: [{ role: 'user', content: 'hello' }],
|
||||
}
|
||||
const req = new Request(
|
||||
`http://localhost:3456/proxy/providers/${provider.id}/v1/messages`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
)
|
||||
const res = await handleProxyRequest(req, new URL(req.url))
|
||||
|
||||
expect(res.status).toBe(200)
|
||||
expect(timeoutCalls).toEqual([45_000])
|
||||
} finally {
|
||||
AbortSignal.timeout = originalTimeout
|
||||
globalThis.fetch = originalFetch
|
||||
}
|
||||
})
|
||||
|
||||
test('uses configured AI request timeout for non-stream Responses upstream requests', async () => {
|
||||
await fs.writeFile(
|
||||
path.join(tmpDir, 'settings.json'),
|
||||
JSON.stringify({
|
||||
network: {
|
||||
aiRequestTimeoutMs: 45_000,
|
||||
proxy: { mode: 'system', url: '' },
|
||||
},
|
||||
}),
|
||||
'utf-8',
|
||||
)
|
||||
|
||||
const svc = new ProviderService()
|
||||
const provider = await svc.addProvider({
|
||||
presetId: 'custom',
|
||||
name: 'OpenAI Responses Proxy',
|
||||
baseUrl: 'https://api.example.com',
|
||||
apiKey: 'sk-test',
|
||||
apiFormat: 'openai_responses',
|
||||
models: {
|
||||
main: 'model-main',
|
||||
haiku: 'model-main',
|
||||
sonnet: 'model-main',
|
||||
opus: 'model-main',
|
||||
},
|
||||
})
|
||||
|
||||
const originalFetch = globalThis.fetch
|
||||
const originalTimeout = AbortSignal.timeout
|
||||
const timeoutCalls: number[] = []
|
||||
globalThis.fetch = mock(async (_url: string | URL | Request, _init?: RequestInit) => {
|
||||
return new Response(JSON.stringify({
|
||||
id: 'resp-network-timeout',
|
||||
status: 'completed',
|
||||
model: 'model-main',
|
||||
output: [{
|
||||
type: 'message',
|
||||
content: [{ type: 'output_text', text: 'ok' }],
|
||||
}],
|
||||
usage: { input_tokens: 1, output_tokens: 1 },
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
}) as typeof fetch
|
||||
AbortSignal.timeout = ((ms: number) => {
|
||||
timeoutCalls.push(ms)
|
||||
return originalTimeout(ms)
|
||||
}) as typeof AbortSignal.timeout
|
||||
|
||||
try {
|
||||
const body = {
|
||||
model: 'model-main',
|
||||
max_tokens: 64,
|
||||
messages: [{ role: 'user', content: 'hello' }],
|
||||
}
|
||||
const req = new Request(
|
||||
`http://localhost:3456/proxy/providers/${provider.id}/v1/messages`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
},
|
||||
)
|
||||
const res = await handleProxyRequest(req, new URL(req.url))
|
||||
|
||||
expect(res.status).toBe(200)
|
||||
expect(timeoutCalls).toEqual([45_000])
|
||||
} finally {
|
||||
AbortSignal.timeout = originalTimeout
|
||||
globalThis.fetch = originalFetch
|
||||
}
|
||||
})
|
||||
|
||||
test('uses configured AI request timeout for streaming upstream requests', async () => {
|
||||
await fs.writeFile(
|
||||
path.join(tmpDir, 'settings.json'),
|
||||
|
||||
@ -134,7 +134,7 @@ async function handleOpenaiChat(
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: signClaudeCodeCCHInTransformedString(JSON.stringify(transformed)),
|
||||
signal: AbortSignal.timeout(isStream ? aiRequestTimeoutMs : Math.max(aiRequestTimeoutMs, 300_000)),
|
||||
signal: AbortSignal.timeout(aiRequestTimeoutMs),
|
||||
...proxyOptions,
|
||||
})
|
||||
|
||||
@ -202,7 +202,7 @@ async function handleOpenaiResponses(
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: signClaudeCodeCCHInTransformedString(JSON.stringify(transformed)),
|
||||
signal: AbortSignal.timeout(isStream ? aiRequestTimeoutMs : Math.max(aiRequestTimeoutMs, 300_000)),
|
||||
signal: AbortSignal.timeout(aiRequestTimeoutMs),
|
||||
...proxyOptions,
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user