Files
2026-07-13 13:39:12 +08:00

66 lines
2.2 KiB
TypeScript

import { getIdempotencyKey, checkIdempotency } from "@/lib/idempotencyLayer";
import { calculateCost } from "@/lib/usage/costCalculator";
import { attachOmniRouteMetaHeaders } from "@/domain/omnirouteResponseMeta";
/**
* Resolve the request's idempotency key once and check the idempotency store. Returns the
* resolved `idempotencyKey` alongside the cache `hit` so the caller can reuse the SAME key
* for the later save path instead of re-deriving it — eliminating the dual-derivation that
* the chatCore modularization (#3598) introduced. (#3821-review LEDGER-6)
*/
export async function checkIdempotencyCache({
clientRawRequest,
provider,
model,
effectiveServiceTier,
startTime,
log,
}: {
clientRawRequest: unknown;
provider: string;
model: string;
effectiveServiceTier: unknown;
startTime: number;
log: unknown;
}): Promise<{ hit: { success: true; response: Response } | null; idempotencyKey: string }> {
const idempotencyKey = getIdempotencyKey(clientRawRequest?.headers);
const cachedIdemp = checkIdempotency(idempotencyKey);
if (cachedIdemp) {
log?.debug?.("IDEMPOTENCY", `Hit for key=${idempotencyKey?.slice(0, 12)}...`);
const idempotentUsage =
cachedIdemp.response && typeof cachedIdemp.response === "object"
? ((cachedIdemp.response as Record<string, unknown>).usage as
| Record<string, unknown>
| undefined)
: undefined;
const idempotentCost = idempotentUsage
? await calculateCost(provider, model, idempotentUsage as Record<string, number>, {
serviceTier: effectiveServiceTier,
})
: 0;
const headers: Record<string, string> = {
"Content-Type": "application/json",
"X-OmniRoute-Idempotent": "true",
};
attachOmniRouteMetaHeaders(headers, {
provider,
model,
cacheHit: false,
latencyMs: Date.now() - startTime,
usage: idempotentUsage,
costUsd: idempotentCost,
});
return {
idempotencyKey,
hit: {
success: true,
response: new Response(JSON.stringify(cachedIdemp.response), {
status: cachedIdemp.status,
headers,
}),
},
};
}
return { hit: null, idempotencyKey };
}