mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-19 13:33:35 +08:00
Desktop injected a far stricter timeout stack than the terminal CLI, so healthy-but-slow third-party providers (sensenova/bailian/zhipu) died at exactly API_TIMEOUT_MS while the UI showed "running" forever: - API_TIMEOUT_MS is the SDK client's time-to-first-byte budget for streaming requests; these gateways send zero bytes (no headers, no SSE ping) until prefill finishes, which takes minutes at large contexts. Raise the default from 120s to the SDK's own 600s and widen the configurable range to 30-1800s. - Widen the desktop-forced stream watchdog idle window to 240s so silent thinking/prefill phases stop tripping the 90s default. - Disable the non-streaming fallback for desktop CLI sessions: a non-streaming request only responds after the FULL generation, so it can never finish inside the same budget and loops timeout aborts forever while the UI spins (also avoids double tool execution, upstream inc-4258). All three knobs respect caller env overrides. Repro: mock upstream whose SSE stays silent for 150s before a complete event sequence — terminal env completes; desktop env aborts at exactly 120s (client timeout) or 90s watchdog + non-streaming fallback loop; the fixed env completes both variants. Tested: bun test src/server/__tests__/network-settings.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/proxy-network-settings.test.ts Tested: cd desktop && bun run test -- src/stores/settingsStore.test.ts src/__tests__/generalSettings.test.tsx Tested: cd desktop && bun run lint Confidence: high Scope-risk: medium
119 lines
3.3 KiB
TypeScript
119 lines
3.3 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
|
|
import * as fs from 'fs/promises'
|
|
import * as os from 'os'
|
|
import * as path from 'path'
|
|
import {
|
|
DEFAULT_AI_REQUEST_TIMEOUT_MS,
|
|
MAX_AI_REQUEST_TIMEOUT_MS,
|
|
MIN_AI_REQUEST_TIMEOUT_MS,
|
|
getManualNetworkProxyUrl,
|
|
buildNetworkEnvironment,
|
|
loadNetworkSettings,
|
|
normalizeNetworkSettings,
|
|
} from '../services/networkSettings.js'
|
|
import { resetSettingsCache } from '../../utils/settings/settingsCache.js'
|
|
|
|
let tmpDir: string
|
|
let originalConfigDir: string | undefined
|
|
|
|
async function setup() {
|
|
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'network-settings-test-'))
|
|
originalConfigDir = process.env.CLAUDE_CONFIG_DIR
|
|
process.env.CLAUDE_CONFIG_DIR = tmpDir
|
|
resetSettingsCache()
|
|
}
|
|
|
|
async function teardown() {
|
|
if (originalConfigDir !== undefined) {
|
|
process.env.CLAUDE_CONFIG_DIR = originalConfigDir
|
|
} else {
|
|
delete process.env.CLAUDE_CONFIG_DIR
|
|
}
|
|
resetSettingsCache()
|
|
await fs.rm(tmpDir, { recursive: true, force: true })
|
|
}
|
|
|
|
describe('network settings', () => {
|
|
beforeEach(setup)
|
|
afterEach(teardown)
|
|
|
|
it('normalizes missing settings to the 600s system-proxy default', () => {
|
|
expect(normalizeNetworkSettings({})).toEqual({
|
|
aiRequestTimeoutMs: DEFAULT_AI_REQUEST_TIMEOUT_MS,
|
|
proxy: {
|
|
mode: 'system',
|
|
url: '',
|
|
},
|
|
})
|
|
})
|
|
|
|
it('clamps AI request timeouts and trims manual proxy URLs', () => {
|
|
expect(normalizeNetworkSettings({
|
|
network: {
|
|
aiRequestTimeoutMs: 9_999_999,
|
|
proxy: {
|
|
mode: 'manual',
|
|
url: ' http://127.0.0.1:7890 ',
|
|
},
|
|
},
|
|
})).toEqual({
|
|
aiRequestTimeoutMs: MAX_AI_REQUEST_TIMEOUT_MS,
|
|
proxy: {
|
|
mode: 'manual',
|
|
url: 'http://127.0.0.1:7890',
|
|
},
|
|
})
|
|
|
|
expect(normalizeNetworkSettings({
|
|
network: {
|
|
aiRequestTimeoutMs: 100,
|
|
},
|
|
}).aiRequestTimeoutMs).toBe(MIN_AI_REQUEST_TIMEOUT_MS)
|
|
})
|
|
|
|
it('loads persisted user network settings for provider requests', async () => {
|
|
await fs.writeFile(
|
|
path.join(tmpDir, 'settings.json'),
|
|
JSON.stringify({
|
|
network: {
|
|
aiRequestTimeoutMs: 180_000,
|
|
proxy: {
|
|
mode: 'manual',
|
|
url: ' http://127.0.0.1:7890 ',
|
|
},
|
|
},
|
|
}),
|
|
'utf-8',
|
|
)
|
|
|
|
const settings = await loadNetworkSettings()
|
|
|
|
expect(settings.aiRequestTimeoutMs).toBe(180_000)
|
|
expect(getManualNetworkProxyUrl(settings)).toBe('http://127.0.0.1:7890')
|
|
expect(buildNetworkEnvironment(settings)).toEqual({
|
|
API_TIMEOUT_MS: '180000',
|
|
HTTP_PROXY: 'http://127.0.0.1:7890',
|
|
HTTPS_PROXY: 'http://127.0.0.1:7890',
|
|
http_proxy: 'http://127.0.0.1:7890',
|
|
https_proxy: 'http://127.0.0.1:7890',
|
|
})
|
|
})
|
|
|
|
it('preserves authenticated manual proxy URLs for provider requests', () => {
|
|
const settings = normalizeNetworkSettings({
|
|
network: {
|
|
proxy: {
|
|
mode: 'manual',
|
|
url: ' https://user:p%40ss@proxy.example.com:8443 ',
|
|
},
|
|
},
|
|
})
|
|
|
|
expect(getManualNetworkProxyUrl(settings)).toBe('https://user:p%40ss@proxy.example.com:8443')
|
|
expect(buildNetworkEnvironment(settings)).toMatchObject({
|
|
HTTP_PROXY: 'https://user:p%40ss@proxy.example.com:8443',
|
|
HTTPS_PROXY: 'https://user:p%40ss@proxy.example.com:8443',
|
|
})
|
|
})
|
|
})
|