Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/atlas/llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
// the typed shapes below.

import OpenAI from "openai";
import { Agent as HttpAgent } from "node:http";
import { Agent as HttpsAgent } from "node:https";

import type { CandidateFragment, Classification } from "./types.js";
import { mostRestrictiveSensitivity } from "./types.js";
Expand Down Expand Up @@ -301,9 +303,26 @@ export class OpenAIDistiller implements LlmDistiller {
"mock server for tests.",
);
}
// When a baseURL is configured it is, in this repo, only ever a local
// aimock/proxy server (see the comment above). Under the full test suite
// every aimock-backed file runs its own in-process server in parallel
// (src + the built dist copy, so ~2x the servers), and the OpenAI SDK's
// node-fetch pools keep-alive sockets. Under that contention a server can
// close an idle pooled socket exactly as a request reuses it, which
// node-fetch surfaces as `FetchError: ... Premature close` — a flaky,
// load-dependent failure across every aimock test (deterministic in CI).
// Pinning a non-keep-alive agent for the mock/proxy case forces a fresh
// socket per request, removing the reuse race. Production (no baseURL →
// real OpenAI over HTTPS) keeps the SDK's default keep-alive agent.
const httpAgent = baseURL
? baseURL.startsWith("https:")
? new HttpsAgent({ keepAlive: false })
: new HttpAgent({ keepAlive: false })
: undefined;
this.client = new OpenAI({
apiKey,
...(baseURL ? { baseURL } : {}),
...(httpAgent ? { httpAgent } : {}),
});
}
}
Expand Down