Compare commits

..

1 Commits

Author SHA1 Message Date
adevwithpurpose
fb9984760c fix(open-sse): stop concurrent requests colliding on dedup hash for non-OpenAI formats
computeRequestHash() in requestDedup.ts projected the prompt content from
body.messages only. The dedup site in chatCore.ts hashes the *translated*
(target-format) request body, and non-OpenAI target formats don't carry a
messages field: Gemini-translated bodies use `contents`, Responses-API
bodies use `input`. So for those formats messages was always undefined,
every prompt hashed to the same null-backed value for a given model, and
concurrent requests with different prompts joined the same in-flight
promise -- the second caller silently received the first caller's
response verbatim (#10249).

Fix: project body.messages ?? body.contents ?? body.input ?? null instead
of only body.messages, keeping the rest of the canonical hash projection
unchanged. Genuinely identical concurrent requests still dedupe (the
intended perf behavior); different prompts under Gemini/Responses-API
target formats no longer collide.

Regression test: tests/unit/request-dedup-10249.test.ts reproduces the
two collision scenarios from the plan-file (Gemini `contents`,
Responses-API `input`), confirms the OpenAI `messages` case was already
correct, and asserts identical-request dedup keeps working. Verified
RED (byte-identical hashes 0b24fd88.../dc16d5b7... pre-fix) -> GREEN
(distinct hashes, dedup preserved) against this exact diff.
2026-08-14 22:41:49 -03:00
9 changed files with 143 additions and 220 deletions

View File

@@ -1 +0,0 @@
- fix(proxy-subscriptions): allow local/loopback proxy-subscription fetch URLs (local-first, cloud-metadata still blocked) (#10158)

View File

@@ -0,0 +1 @@
- fix(open-sse): stop concurrent requests colliding on the same dedup hash for non-OpenAI target formats (#10249)

View File

@@ -8435,12 +8435,7 @@ components:
type: string
url:
type: string
description: >-
Redacted subscription URL. May be a local/loopback address
(e.g. `http://127.0.0.1:8080/list`) — local-first fetch targets
are allowed by default (`OMNIROUTE_ALLOW_LOCAL_PROVIDER_URLS`);
cloud-metadata / link-local endpoints (169.254.0.0/16) are always
blocked.
description: Redacted subscription URL.
enabled:
type: boolean
mode:

View File

@@ -36,12 +36,19 @@ const inflight = new Map<string, Promise<unknown>>();
* Compute a deterministic hash for a request body.
* Includes: model, messages, temperature, tools, tool_choice, max_tokens, response_format
* Excludes: stream, user, metadata (don't affect LLM output)
*
* The prompt content can live under different keys depending on the target
* provider format the body has already been translated to: OpenAI-style
* bodies use `messages`, Gemini-translated bodies use `contents`, and
* Responses-API-translated bodies use `input`. Falling back to only
* `messages` made every non-OpenAI-format body hash the prompt as `null`,
* colliding different prompts onto the same dedup hash (#10249).
*/
export function computeRequestHash(requestBody: unknown): string {
const body = requestBody as Record<string, unknown>;
const canonical = {
model: body.model ?? null,
messages: body.messages ?? null,
messages: body.messages ?? body.contents ?? body.input ?? null,
temperature: typeof body.temperature === "number" ? body.temperature : 1.0,
tools: body.tools ?? null,
tool_choice: body.tool_choice ?? null,

View File

@@ -3,57 +3,30 @@
*
* The subscription URL is fetched server-side (see `subscriptionService
* .fetchSubscriptionContent`). Without a guard, an operator — or a compromised
* subscription link — could point OmniRoute at cloud metadata (SSRF). Only
* http/https to non-metadata hosts are allowed.
*
* Local-first (#10158): OmniRoute already lets an operator route ALL traffic
* through a loopback core (`coreEndpoint.ts` allows `127.0.0.1`/`::1`/
* `localhost`), so a subscription fetch target on loopback/private ranges is
* ALLOWED by default (`allowLocal: true`, matching the local-first default of
* `areLocalProviderUrlsAllowed()` in `src/shared/network/outboundUrlGuardPolicy
* .ts`) — mirroring that policy's "block-metadata" mode. Cloud-metadata /
* link-local (`169.254.0.0/16`, incl. `169.254.169.254` IMDS) and the
* unspecified address (`0.0.0.0/8`) are blocked UNCONDITIONALLY regardless of
* `allowLocal`, since they have no legitimate subscription-source use case.
* Callers that want the old strict (public-only) behavior pass
* `{ allowLocal: false }`.
* subscription link — could point OmniRoute at internal services or cloud
* metadata (SSRF). Only http/https to non-internal hosts are allowed:
* loopback / private / link-local (incl. 169.254.0.0/16 cloud metadata) /
* unspecified addresses are blocked.
*
* Hostname resolution is re-checked at fetch time (also using the IP-range
* helpers here) so a hostname that resolves to an internal address is still
* refused. Splitting the logic into pure functions keeps it unit-testable
* without DNS / the full stack. No `@/`-aliased or DB-backed imports here —
* the `allowLocal` policy decision is made by the caller (subscriptionService,
* which is already DB-backed) and passed in as a plain boolean.
* without DNS / the full stack.
*/
/** Only these URL schemes may be used to *fetch* a subscription. */
export const ALLOWED_FETCH_SCHEMES = new Set<string>(["http:", "https:"]);
// Blocked UNCONDITIONALLY, regardless of `allowLocal` — the classic SSRF→cloud
// credential pivot; never a legitimate subscription source.
const ALWAYS_BLOCKED_IPV4: ReadonlyArray<readonly [number, number]> = [
// Blocked IPv4 ranges (base, mask) as 32-bit ints.
const BLOCKED_IPV4: ReadonlyArray<readonly [number, number]> = [
[0x00000000, 0xff000000], // 0.0.0.0/8 unspecified
[0xa9fe0000, 0xffff0000], // 169.254.0.0/16 link-local (incl. cloud metadata IMDS)
];
// Blocked only when `allowLocal` is false (strict/public-only mode).
const LOCAL_ONLY_BLOCKED_IPV4: ReadonlyArray<readonly [number, number]> = [
[0x7f000000, 0xff000000], // 127.0.0.0/8 loopback
[0x0a000000, 0xff000000], // 10.0.0.0/8 private
[0xac100000, 0xfff00000], // 172.16.0.0/12 private
[0xc0a80000, 0xffff0000], // 192.168.0.0/16 private
[0xa9fe0000, 0xffff0000], // 169.254.0.0/16 link-local (cloud metadata)
];
export interface FetchGuardOptions {
/**
* When true (default), loopback/private hosts are allowed as fetch targets
* ("local-first" — matches `areLocalProviderUrlsAllowed()`'s default). Cloud
* metadata / link-local is blocked unconditionally either way. Pass `false`
* to restore the strict public-only behavior.
*/
allowLocal?: boolean;
}
const IPV4_RE = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
export function isIpv4Literal(host: string): boolean {
@@ -71,24 +44,20 @@ export function ipv4ToLong(host: string): number | null {
return (parts[0] * 16777216 + parts[1] * 65536 + parts[2] * 256 + parts[3]) >>> 0;
}
export function isIpv4Blocked(ip: string, opts: FetchGuardOptions = {}): boolean {
const allowLocal = opts.allowLocal ?? true;
export function isIpv4Blocked(ip: string): boolean {
const n = ipv4ToLong(ip);
if (n === null) return false;
// `&` yields a signed 32-bit int; coerce both sides to unsigned before
// comparing so masked results with the high bit set aren't negative.
const ranges = allowLocal ? ALWAYS_BLOCKED_IPV4 : [...ALWAYS_BLOCKED_IPV4, ...LOCAL_ONLY_BLOCKED_IPV4];
return ranges.some(([base, mask]) => ((n & mask) >>> 0) === (base >>> 0));
return BLOCKED_IPV4.some(([base, mask]) => ((n & mask) >>> 0) === (base >>> 0));
}
/** Blocked IPv6 addresses: unspecified/link-local always; loopback/ULA only when strict. */
export function isIpv6Blocked(ip: string, opts: FetchGuardOptions = {}): boolean {
const allowLocal = opts.allowLocal ?? true;
/** Blocked IPv6 addresses: loopback, unspecified, link-local, ULA. */
export function isIpv6Blocked(ip: string): boolean {
const h = ip.toLowerCase();
if (h === "::") return true; // unspecified — always blocked
if (h.startsWith("fe80")) return true; // link-local — always blocked
if (allowLocal) return false;
if (h === "::1") return true; // loopback
if (h === "::") return true; // unspecified
if (h.startsWith("fe80")) return true; // link-local
if (h.startsWith("fc") || h.startsWith("fd")) return true; // unique local
return false;
}
@@ -109,22 +78,20 @@ export function isIpLiteral(host: string): boolean {
* the `dns` module convention (4 = IPv4, 6 = IPv6; missing ⇒ treat as v4).
*/
export function isAnyResolvedAddressBlocked(
addrs: ReadonlyArray<{ address: string; family?: number }>,
opts: FetchGuardOptions = {}
addrs: ReadonlyArray<{ address: string; family?: number }>
): boolean {
return addrs.some(({ address, family }) => {
const fam = family === 6 ? 6 : 4;
return fam === 6 ? isIpv6Blocked(address, opts) : isIpv4Blocked(address, opts);
return fam === 6 ? isIpv6Blocked(address) : isIpv4Blocked(address);
});
}
/**
* Structural check (no DNS). True only if the scheme is allowed AND, when the
* host is an IP literal, it is not in a blocked range for the given
* `allowLocal` mode. Hostnames pass the structural check — they are resolved
* and re-checked at fetch time.
* host is an IP literal, it is not in a blocked range. Hostnames pass the
* structural check — they are resolved and re-checked at fetch time.
*/
export function isSubscriptionFetchUrlAllowed(url: string, opts: FetchGuardOptions = {}): boolean {
export function isSubscriptionFetchUrlAllowed(url: string): boolean {
let u: URL;
try {
u = new URL(url);
@@ -137,8 +104,8 @@ export function isSubscriptionFetchUrlAllowed(url: string, opts: FetchGuardOptio
const host = rawHost.startsWith("[") && rawHost.endsWith("]") ? rawHost.slice(1, -1) : rawHost;
if (host === "") return false;
if (isIpLiteral(host)) {
if (isIpv4Literal(host)) return !isIpv4Blocked(host, opts);
return !isIpv6Blocked(host, opts);
if (isIpv4Literal(host)) return !isIpv4Blocked(host);
return !isIpv6Blocked(host);
}
return true; // hostname: resolved + checked at fetch time
}

View File

@@ -36,9 +36,7 @@ import {
isSubscriptionFetchUrlAllowed,
isIpLiteral,
isAnyResolvedAddressBlocked,
type FetchGuardOptions,
} from "./fetchGuard";
import { areLocalProviderUrlsAllowed } from "@/shared/network/outboundUrlGuardPolicy";
import { withRetry } from "./fetchRetry";
import { parseSubscription, redactedNodeSummary, type ParsedSubscription } from "./parse";
@@ -282,20 +280,13 @@ export async function deleteSubscription(id: string): Promise<boolean> {
// ───────────────────────────── Sync + apply ─────────────────────────────
/**
* Refuse to fetch a subscription URL unless it is http/https to an allowed
* Refuse to fetch a subscription URL unless it is http/https to a non-internal
* host. IP literals are checked structurally; hostnames are resolved and the
* resolved addresses are re-checked (fail closed on resolution errors).
*
* Local-first (#10158): loopback/private fetch targets are ALLOWED when
* `areLocalProviderUrlsAllowed()` is on (default ON — same local-first policy
* already used for provider validation, and consistent with
* `coreEndpoint.ts` already permitting a loopback routing core). Cloud
* metadata / link-local (169.254.0.0/16, incl. 169.254.169.254 IMDS) is
* blocked UNCONDITIONALLY regardless of that flag.
* resolved addresses are re-checked (fail closed on resolution errors). This
* blocks SSRF to internal services / cloud metadata (169.254.169.254).
*/
async function assertSafeFetchTarget(url: string): Promise<void> {
const guardOpts: FetchGuardOptions = { allowLocal: areLocalProviderUrlsAllowed() };
if (!isSubscriptionFetchUrlAllowed(url, guardOpts)) {
if (!isSubscriptionFetchUrlAllowed(url)) {
throw new Error("Subscription URL is not allowed (scheme or host blocked)");
}
const host = new URL(url).hostname.toLowerCase();
@@ -308,7 +299,7 @@ async function assertSafeFetchTarget(url: string): Promise<void> {
try {
const dns = await import("node:dns");
const addrs = await dns.promises.lookup(bare, { all: true });
if (isAnyResolvedAddressBlocked(addrs, guardOpts)) {
if (isAnyResolvedAddressBlocked(addrs)) {
throw new Error("Subscription host resolves to a blocked (internal) address");
}
} catch (e) {

View File

@@ -23,25 +23,9 @@ test("non-http(s) schemes are rejected", () => {
assert.equal(isSubscriptionFetchUrlAllowed("gopher://example.com"), false);
});
test("local-first (#10158): loopback/private IPv4 literals are ALLOWED by default", () => {
for (const ip of ["127.0.0.1", "10.0.0.5", "172.16.0.1", "192.168.1.1"]) {
assert.equal(isSubscriptionFetchUrlAllowed(`https://${ip}/x`), true, ip);
}
});
test("cloud-metadata / link-local / unspecified IPv4 literals are ALWAYS blocked", () => {
for (const ip of ["169.254.169.254", "169.254.1.1", "0.0.0.0"]) {
assert.equal(isSubscriptionFetchUrlAllowed(`https://${ip}/x`), false, ip);
}
});
test("strict mode (allowLocal: false) rejects loopback/private IPv4 literals", () => {
test("blocked IPv4 literals are rejected", () => {
for (const ip of ["127.0.0.1", "10.0.0.5", "172.16.0.1", "192.168.1.1", "169.254.169.254", "0.0.0.0"]) {
assert.equal(
isSubscriptionFetchUrlAllowed(`https://${ip}/x`, { allowLocal: false }),
false,
ip
);
assert.equal(isSubscriptionFetchUrlAllowed(`https://${ip}/x`), false, ip);
}
});
@@ -50,25 +34,9 @@ test("public IPv4 literals are allowed", () => {
assert.equal(isSubscriptionFetchUrlAllowed("http://1.1.1.1/"), true);
});
test("local-first (#10158): loopback/ULA IPv6 literals are ALLOWED by default", () => {
for (const ip of ["::1", "fc00::1", "fd12:3456::1"]) {
assert.equal(isSubscriptionFetchUrlAllowed(`https://[${ip}]/x`), true, ip);
}
});
test("unspecified / link-local IPv6 literals are ALWAYS blocked", () => {
for (const ip of ["::", "fe80::1"]) {
assert.equal(isSubscriptionFetchUrlAllowed(`https://[${ip}]/x`), false, ip);
}
});
test("strict mode (allowLocal: false) rejects loopback/ULA IPv6 literals", () => {
test("blocked IPv6 literals are rejected (bracketed)", () => {
for (const ip of ["::1", "::", "fe80::1", "fc00::1", "fd12:3456::1"]) {
assert.equal(
isSubscriptionFetchUrlAllowed(`https://[${ip}]/x`, { allowLocal: false }),
false,
ip
);
assert.equal(isSubscriptionFetchUrlAllowed(`https://[${ip}]/x`), false, ip);
}
});
@@ -78,13 +46,11 @@ test("malformed / empty-host URLs are rejected", () => {
assert.equal(isSubscriptionFetchUrlAllowed("http://?x"), false); // empty host
});
test("ip-range + literal helpers (local-first defaults)", () => {
assert.equal(isIpv4Blocked("127.0.0.1"), false); // allowed by default (local-first)
assert.equal(isIpv4Blocked("127.0.0.1", { allowLocal: false }), true);
assert.equal(isIpv4Blocked("169.254.169.254"), true); // always blocked
test("ip-range + literal helpers", () => {
assert.equal(isIpv4Blocked("127.0.0.1"), true);
assert.equal(isIpv4Blocked("169.254.169.254"), true);
assert.equal(isIpv4Blocked("8.8.8.8"), false);
assert.equal(isIpv6Blocked("::1"), false); // allowed by default (local-first)
assert.equal(isIpv6Blocked("::1", { allowLocal: false }), true);
assert.equal(isIpv6Blocked("::1"), true);
assert.equal(isIpv6Blocked("2606:4700::1111"), false);
assert.equal(isIpLiteral("127.0.0.1"), true);
assert.equal(isIpLiteral("::1"), true);
@@ -92,22 +58,14 @@ test("ip-range + literal helpers (local-first defaults)", () => {
assert.deepEqual([...ALLOWED_FETCH_SCHEMES], ["http:", "https:"]);
});
test("multi-record DNS: blocks if ANY resolved address is metadata/link-local (local-first default)", () => {
// A private address alongside a public one is now ALLOWED by default
// (local-first) — only cloud-metadata/link-local addresses stay blocked.
test("multi-record DNS: blocks if ANY resolved address is internal", () => {
// Hostname resolves to a public AND a private address — must be refused
// (closes the first-address-only bypass).
assert.equal(
isAnyResolvedAddressBlocked([
{ address: "8.8.8.8", family: 4 },
{ address: "192.168.1.10", family: 4 },
]),
false
);
// A cloud-metadata address among public records → still blocked.
assert.equal(
isAnyResolvedAddressBlocked([
{ address: "8.8.8.8", family: 4 },
{ address: "169.254.169.254", family: 4 },
]),
true
);
// All public → allowed.
@@ -118,45 +76,14 @@ test("multi-record DNS: blocks if ANY resolved address is metadata/link-local (l
]),
false
);
// Strict mode (allowLocal: false): a private address is blocked again.
// A single internal IPv6 among public records → blocked.
assert.equal(
isAnyResolvedAddressBlocked(
[
{ address: "8.8.8.8", family: 4 },
{ address: "192.168.1.10", family: 4 },
],
{ allowLocal: false }
),
true
);
// A single internal IPv6 among public records → blocked in strict mode.
assert.equal(
isAnyResolvedAddressBlocked(
[
{ address: "2606:4700::1111", family: 6 },
{ address: "fd00::1", family: 6 },
],
{ allowLocal: false }
),
isAnyResolvedAddressBlocked([
{ address: "2606:4700::1111", family: 6 },
{ address: "fd00::1", family: 6 },
]),
true
);
// Empty result set → nothing blocked.
assert.equal(isAnyResolvedAddressBlocked([]), false);
});
// ─────────────────── Regression: #10158 local proxy subscription ───────────────────
// Promoted from the TDD probe (was RED on release/v3.8.50: local http subscription
// URLs were rejected by the strict SSRF guard even though the same feature already
// permits loopback for the routing half — coreEndpoint.ts's ALLOWED_LOCAL_CORE_HOSTS).
test("#10158: local (127.0.0.1) http subscription URL is allowed by default", () => {
assert.equal(
isSubscriptionFetchUrlAllowed("http://127.0.0.1:8080/list"),
true,
"an operator should be able to fetch a proxy list from a local HTTP server"
);
});
test("#10158: IMDS / cloud-metadata pivot stays blocked even with local-first default", () => {
assert.equal(isSubscriptionFetchUrlAllowed("http://169.254.169.254/latest/meta-data/"), false);
});

View File

@@ -3,7 +3,6 @@ import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import http from "node:http";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-sub-svc-"));
process.env.DATA_DIR = TEST_DATA_DIR;
@@ -230,57 +229,3 @@ test("global→rule switch re-evaluates binding: drops global, binds the selecte
assert.ok(afterRule, "rule mode should bind the node to provider provA");
assert.equal(afterRule?.proxy.host, "10.0.0.5");
});
// ─────────────────── Regression: #10158 local proxy subscription ───────────────────
// Promoted from the TDD probe (was RED on release/v3.8.50: createSubscription against a
// real local (127.0.0.1) HTTP server failed with "Fetch failed: Subscription URL is not
// allowed (scheme or host blocked)" even though coreEndpoint.ts already permits routing
// through a loopback core). Uses a REAL local HTTP server (not a fetch stub) so the fix
// is proven end-to-end through assertSafeFetchTarget's SSRF guard.
function startLocalSubscriptionServer(
body: string
): Promise<{ url: string; close: () => Promise<void> }> {
return new Promise((resolve) => {
const srv = http.createServer((_req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end(body);
});
srv.listen(0, "127.0.0.1", () => {
const addr = srv.address();
if (!addr || typeof addr === "string") throw new Error("no addr");
resolve({
url: `http://127.0.0.1:${addr.port}/list`,
close: () => new Promise((r) => srv.close(() => r())),
});
});
});
}
test("#10158: createSubscription against a real local (127.0.0.1) HTTP server syncs ok", async () => {
await reset();
const LIST_BODY = [
"proxies:",
" - name: local-node",
" type: http",
" server: 127.0.0.1",
" port: 8080",
].join("\n");
const { url, close } = await startLocalSubscriptionServer(LIST_BODY);
try {
const created = await sub.createSubscription({
name: "local-list",
url,
enabled: true,
mode: "global",
});
assert.equal(
created.status,
"ok",
`expected ok, got status=${created.status} error=${created.error}`
);
assert.ok((created.lastNodes ?? []).length >= 1, "expected at least one parsed node");
} finally {
await close();
}
});

View File

@@ -0,0 +1,91 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { computeRequestHash, deduplicate, clearInflight } from "../../open-sse/services/requestDedup.ts";
// Regression tests for #10249: the dedup hash used to read only `body.messages`,
// so translated (target-format) bodies that carry the prompt under a different
// key (`contents` for Gemini, `input` for the Responses API) always hashed the
// prompt as `null`. Concurrent requests with different prompts then collided on
// the same dedup hash, joined the same in-flight promise, and the second caller
// silently received the first caller's response.
test("Gemini-format translated bodies with different prompts must NOT collide on dedup hash", async () => {
clearInflight();
const bodyA = {
contents: [{ role: "user", parts: [{ text: "Summarize the Q3 financial report attached." }] }],
temperature: 0,
};
const bodyB = {
contents: [{ role: "user", parts: [{ text: "Extract every invoice number from the attached PDF." }] }],
temperature: 0,
};
const hashA = computeRequestHash({ ...bodyA, model: "gemini/gemini-2.5-flash", stream: false });
const hashB = computeRequestHash({ ...bodyB, model: "gemini/gemini-2.5-flash", stream: false });
assert.notEqual(hashA, hashB, "Different prompts must have different dedup hashes");
const [resA, resB] = await Promise.all([
deduplicate(hashA, async () => "RESPONSE_A"),
deduplicate(hashB, async () => "RESPONSE_B"),
]);
assert.equal(resA.result, "RESPONSE_A");
assert.equal(resB.result, "RESPONSE_B");
assert.equal(resB.wasDeduplicated, false);
});
test("Responses-API input-format translated bodies with different prompts must NOT collide", async () => {
clearInflight();
const bodyA = {
input: [{ role: "user", content: [{ type: "input_text", text: "What is the capital of France?" }] }],
temperature: 0,
};
const bodyB = {
input: [{ role: "user", content: [{ type: "input_text", text: "Explain quantum entanglement." }] }],
temperature: 0,
};
const hashA = computeRequestHash({ ...bodyA, model: "openai/gpt-4.1", stream: false });
const hashB = computeRequestHash({ ...bodyB, model: "openai/gpt-4.1", stream: false });
assert.notEqual(hashA, hashB, "Different prompts must have different dedup hashes");
const [resA, resB] = await Promise.all([
deduplicate(hashA, async () => "RESPONSE_A"),
deduplicate(hashB, async () => "RESPONSE_B"),
]);
assert.equal(resA.result, "RESPONSE_A");
assert.equal(resB.result, "RESPONSE_B");
assert.equal(resB.wasDeduplicated, false);
});
test("Sanity: OpenAI-format bodies with different prompts DO get distinct hashes (unchanged behavior)", () => {
const bodyA = { messages: [{ role: "user", content: "Hello there" }], temperature: 0 };
const bodyB = { messages: [{ role: "user", content: "Goodbye now" }], temperature: 0 };
const hashA = computeRequestHash({ ...bodyA, model: "openai/gpt-4.1", stream: false });
const hashB = computeRequestHash({ ...bodyB, model: "openai/gpt-4.1", stream: false });
assert.notEqual(hashA, hashB);
});
test("Genuinely identical requests still hash identically and get deduplicated (perf feature preserved)", async () => {
clearInflight();
const body = {
contents: [{ role: "user", parts: [{ text: "Same prompt text every time" }] }],
temperature: 0,
};
const hash1 = computeRequestHash({ ...body, model: "gemini/gemini-2.5-flash", stream: false });
const hash2 = computeRequestHash({ ...body, model: "gemini/gemini-2.5-flash", stream: false });
assert.equal(hash1, hash2, "Identical bodies must still produce the same hash");
let callCount = 0;
const slowFn = async () => {
callCount += 1;
await new Promise((resolve) => setTimeout(resolve, 20));
return "SHARED_RESPONSE";
};
const [resA, resB] = await Promise.all([
deduplicate(hash1, slowFn),
deduplicate(hash2, slowFn),
]);
assert.equal(resA.result, "SHARED_RESPONSE");
assert.equal(resB.result, "SHARED_RESPONSE");
assert.equal(callCount, 1, "Identical concurrent requests must share a single upstream call");
assert.equal(resA.wasDeduplicated === true || resB.wasDeduplicated === true, true);
});