mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(safe-fetch): leave the deadline to the caller's signal
The pinned dispatchers kept undici's own 300 s header and body timers. A local model with the response timeout set above five minutes answers a non-streamed completion only once it is done, so its headers could take longer than 300 s and undici dropped a call the caller still allowed; the failure read as a bare network error. Every safeFetch call carries its own AbortSignal.timeout, so that signal is now the one deadline. Refs #1267
This commit is contained in:
1 parent
ed04103eb6
commit
bb3ee6b855
2 files changed
+51
No files matched your search
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* #1267 — the pinned dispatchers leave the deadline to the caller's signal.
|
||||
*
|
||||
* undici's Agent arms its own 300 s header timer and 300 s body timer by
|
||||
* default. A local model with the response timeout at 600 s answers a
|
||||
* non-streamed completion only once it is done, so its headers can arrive
|
||||
* after 300 s: undici then dropped a call the caller still allowed, and the
|
||||
* failure read as a bare network error. Waiting five minutes in a unit test
|
||||
* proves nothing faster, so this reads the options the Agent was built with.
|
||||
*/
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import type { Agent } from "undici";
|
||||
import {
|
||||
getPinnedOperatorApprovedDispatcher,
|
||||
getPinnedPublicDispatcher,
|
||||
_resetPinnedDispatcherForTests,
|
||||
} from "../safe-fetch-dispatcher";
|
||||
|
||||
function agentOptions(agent: Agent): {
|
||||
headersTimeout?: number;
|
||||
bodyTimeout?: number;
|
||||
} {
|
||||
const key = Object.getOwnPropertySymbols(agent).find(
|
||||
(s) => s.description === "options",
|
||||
);
|
||||
expect(key).toBeDefined();
|
||||
return (agent as unknown as Record<symbol, Record<string, number>>)[key!];
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
_resetPinnedDispatcherForTests();
|
||||
});
|
||||
|
||||
describe("pinned dispatcher timers", () => {
|
||||
it.each([
|
||||
["public", getPinnedPublicDispatcher],
|
||||
["operator-approved", getPinnedOperatorApprovedDispatcher],
|
||||
])("the %s dispatcher arms no header or body timer of its own", (_, get) => {
|
||||
const options = agentOptions(get());
|
||||
expect(options.headersTimeout).toBe(0);
|
||||
expect(options.bodyTimeout).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -147,6 +147,14 @@ let cachedOperatorApproved: Agent | null = null;
|
||||
|
||||
function createPinnedDispatcher(policy: AddressPolicy): Agent {
|
||||
return new Agent({
|
||||
// #1267 — undici's own 300 s header and body timers would cut a call the
|
||||
// caller allowed longer: a local model with a 600 s response timeout
|
||||
// answers a non-streamed completion only when it is done, so its headers
|
||||
// can take longer than 300 s, and the failure surfaced as a bare
|
||||
// "network" error. Every `safeFetch` call carries its own
|
||||
// `AbortSignal.timeout`, so that signal is the one deadline.
|
||||
headersTimeout: 0,
|
||||
bodyTimeout: 0,
|
||||
connect: {
|
||||
lookup:
|
||||
policy === "public" ? pinnedPublicLookup : pinnedOperatorApprovedLookup,
|
||||
|
||||
Reference in new issue
Block a user