mirror of
https://github.com/THU-MAIC/OpenMAIC.git
synced 2026-10-28 08:25:57 +08:00
fix(web-fetch): let DeepSeek fetch wait for headers until the request deadline (#1990)
DeepSeek's fetch endpoint sends response headers only after it has fetched the whole page, so the fixed 10s header timeout aborted slow pages that the provider still completed and billed. Use the request deadline (bodyTimeoutMs, default 30s) for the header wait, as the Firecrawl provider already does. Fixes #1984
This commit is contained in:
2 files changed
+16
-8
No files matched your search
@@ -52,22 +52,25 @@ export const deepseek: Extract<WebFetchProvider, { hosted: true }> = {
|
||||
throw new Error('Invalid DeepSeek fetch URL');
|
||||
const credentials = deepseek.resolveCredentials(options.credentials);
|
||||
if (!credentials.apiKey) throw new Error('Web fetch provider requires an API key');
|
||||
// DeepSeek sends response headers only after it has fetched the whole page, so
|
||||
// the header wait shares the request deadline instead of a shorter one.
|
||||
const deadlineMs = options.bodyTimeoutMs ?? 30_000;
|
||||
const signal = options.signal
|
||||
? AbortSignal.any([options.signal, AbortSignal.timeout(options.bodyTimeoutMs ?? 30_000)])
|
||||
: AbortSignal.timeout(options.bodyTimeoutMs ?? 30_000);
|
||||
? AbortSignal.any([options.signal, AbortSignal.timeout(deadlineMs)])
|
||||
: AbortSignal.timeout(deadlineMs);
|
||||
signal.throwIfAborted();
|
||||
const { Agent } = await import('undici');
|
||||
const { proxyFetch } = await import('@/lib/server/proxy-fetch');
|
||||
const agent = new Agent({
|
||||
connect: { timeout: 5_000 },
|
||||
headersTimeout: 10_000,
|
||||
bodyTimeout: options.bodyTimeoutMs ?? 30_000,
|
||||
headersTimeout: deadlineMs,
|
||||
bodyTimeout: deadlineMs,
|
||||
});
|
||||
const headersDeadline = new AbortController();
|
||||
const requestSignal = AbortSignal.any([signal, headersDeadline.signal]);
|
||||
const headerTimer = setTimeout(
|
||||
() => headersDeadline.abort(new Error('Web fetch headers timed out')),
|
||||
10_000,
|
||||
deadlineMs,
|
||||
);
|
||||
headerTimer.unref?.();
|
||||
try {
|
||||
@@ -94,8 +97,8 @@ export const deepseek: Extract<WebFetchProvider, { hosted: true }> = {
|
||||
proxy: credentials.proxy,
|
||||
timeouts: {
|
||||
connectTimeout: 5_000,
|
||||
headersTimeout: 10_000,
|
||||
bodyTimeout: options.bodyTimeoutMs ?? 30_000,
|
||||
headersTimeout: deadlineMs,
|
||||
bodyTimeout: deadlineMs,
|
||||
},
|
||||
},
|
||||
),
|
||||
|
||||
@@ -224,7 +224,7 @@ it('forwards an operator proxy while retaining transport deadlines', async () =>
|
||||
});
|
||||
expect(proxyFetch).toHaveBeenLastCalledWith(expect.any(String), expect.anything(), {
|
||||
proxy: 'http://proxy.example:8080',
|
||||
timeouts: { connectTimeout: 5000, headersTimeout: 10000, bodyTimeout: 30000 },
|
||||
timeouts: { connectTimeout: 5000, headersTimeout: 30000, bodyTimeout: 30000 },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -252,7 +252,12 @@ it('shares the original header deadline across transport attempts', async () =>
|
||||
.mockImplementationOnce(() => new Promise(() => {}));
|
||||
vi.stubGlobal('fetch', fetch);
|
||||
const result = deepseek.extract(url, { credentials: { apiKey: 'key' } });
|
||||
let settled = false;
|
||||
result.catch(() => undefined).finally(() => (settled = true));
|
||||
const assertion = expect(result).rejects.toThrow('Web fetch headers timed out');
|
||||
// DeepSeek answers only after fetching the page, so 20s must not hit the header deadline.
|
||||
await vi.advanceTimersByTimeAsync(20_000);
|
||||
expect(settled).toBe(false);
|
||||
await vi.advanceTimersByTimeAsync(10_000);
|
||||
await assertion;
|
||||
expect(fetch).toHaveBeenCalledTimes(2);
|
||||
|
||||
Reference in new issue
Block a user