mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-20 13:52:28 +03:00
feat(video): redact transcript fields in the in-memory pending-request snapshot (#12430 item 6)
trackPendingRequest (open-sse/handlers/chatCore.ts) stored the raw client body (with video transcript/audioTranscript cues) under `clientRequest`, live-exposed via /api/usage/call-logs (pendingDetails), /api/logs/[id] and /api/conversations while a request is in-flight. P2a redacted the persisted detailed-log snapshot but not this in-memory copy. Add redactPendingBody() to videoBridgeSnapshotRedaction.ts (sibling to logClientRawRequestRedacted from P2a): when videoBridgeObserved, returns the redacted clone from redactVideoTranscriptFieldsForLog; otherwise returns the exact same reference. Wire it into the trackPendingRequest call site (chatCore.ts:934), keeping the file within its frozen 5976-line budget (5971 -> 5974).
This commit is contained in:
@@ -360,7 +360,10 @@ import { deleteSessionAccountAffinity } from "@/lib/db/sessionAccountAffinity";
|
||||
import { getCacheControlSettings } from "@/lib/cacheControlSettings";
|
||||
import { guardrailRegistry } from "@/lib/guardrails";
|
||||
import type { VideoBridgeLogRedactionEntry } from "@/lib/guardrails/videoBridge";
|
||||
import { logClientRawRequestRedacted } from "@/lib/guardrails/videoBridgeSnapshotRedaction";
|
||||
import {
|
||||
logClientRawRequestRedacted,
|
||||
redactPendingBody,
|
||||
} from "@/lib/guardrails/videoBridgeSnapshotRedaction";
|
||||
import {
|
||||
shouldPreserveCacheControl,
|
||||
resolveConnectionCacheOverride,
|
||||
@@ -928,7 +931,7 @@ export async function handleChatCore({
|
||||
const pendingRequestId =
|
||||
trackPendingRequest(model, provider, pendingConnId, true, {
|
||||
clientEndpoint: clientRawRequest?.endpoint || "/v1/chat/completions",
|
||||
clientRequest: clientRawRequest?.body ?? body,
|
||||
clientRequest: redactPendingBody(clientRawRequest?.body ?? body, videoBridgeObserved),
|
||||
providerRequest: initialProviderRequest,
|
||||
stage: "registered",
|
||||
correlationId,
|
||||
|
||||
@@ -133,3 +133,17 @@ export function logClientRawRequestRedacted(
|
||||
clientRawRequest.headers
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call-site wrapper for the `clientRequest` field stored by `trackPendingRequest`
|
||||
* (open-sse/handlers/chatCore.ts): the sibling in-memory leak to
|
||||
* `logClientRawRequestRedacted` above — same raw body, but live-exposed via
|
||||
* /api/usage/call-logs (pendingDetails), /api/logs/[id] and /api/conversations
|
||||
* while the request is in-flight, not just in the persisted detailed-log
|
||||
* snapshot. Identical observed/non-observed branching: a non-observed request
|
||||
* keeps the exact same reference (no clone); an observed one gets the redacted
|
||||
* clone.
|
||||
*/
|
||||
export function redactPendingBody(clientRequest: unknown, videoBridgeObserved: boolean): unknown {
|
||||
return videoBridgeObserved ? redactVideoTranscriptFieldsForLog(clientRequest) : clientRequest;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { redactVideoTranscriptFieldsForLog } from "../../../src/lib/guardrails/videoBridgeSnapshotRedaction.ts";
|
||||
import {
|
||||
redactVideoTranscriptFieldsForLog,
|
||||
redactPendingBody,
|
||||
} from "../../../src/lib/guardrails/videoBridgeSnapshotRedaction.ts";
|
||||
// Heavy import is fine here (test only, never in the production module under test) — used
|
||||
// solely to prove the local placeholder literal never drifts from the canonical P1 constant.
|
||||
import { VIDEO_TRANSCRIPT_REDACTION_PLACEHOLDER } from "../../../src/lib/guardrails/videoBridgeHelpers.ts";
|
||||
@@ -205,3 +208,57 @@ test("the redaction placeholder matches the canonical P1 constant (no drift)", (
|
||||
const part = contentAt(result, "messages", 0)[0];
|
||||
assert.equal(part.transcript, VIDEO_TRANSCRIPT_REDACTION_PLACEHOLDER);
|
||||
});
|
||||
|
||||
// #12430 item 6 (P2c): the sibling in-memory leak. `trackPendingRequest`
|
||||
// (open-sse/handlers/chatCore.ts) stores the same raw client body under
|
||||
// `clientRequest`, live-exposed via /api/usage/call-logs (pendingDetails),
|
||||
// /api/logs/[id] and /api/conversations while the request is in-flight. This
|
||||
// helper is the guarded call-site wrapper chatCore.ts uses, mirroring
|
||||
// logClientRawRequestRedacted's observed/non-observed branching.
|
||||
test("redactPendingBody: observed=true delegates to redactVideoTranscriptFieldsForLog", () => {
|
||||
const body = {
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "input_video",
|
||||
video_url: "https://example.com/clip.mp4",
|
||||
transcript: { cues: [{ text: "pending secret" }] },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = redactPendingBody(body, true);
|
||||
assert.notEqual(
|
||||
result,
|
||||
body,
|
||||
"observed path must return a new structure, not the same reference"
|
||||
);
|
||||
const part = contentAt(result, "messages", 0)[0];
|
||||
assert.equal(part.transcript, VIDEO_TRANSCRIPT_REDACTION_PLACEHOLDER);
|
||||
assert.ok(!JSON.stringify(result).includes("pending secret"));
|
||||
assert.deepEqual(result, redactVideoTranscriptFieldsForLog(body));
|
||||
});
|
||||
|
||||
test("redactPendingBody: observed=false returns the SAME reference unchanged", () => {
|
||||
const body = {
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "input_video",
|
||||
video_url: "https://example.com/clip.mp4",
|
||||
transcript: { cues: [{ text: "not observed" }] },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = redactPendingBody(body, false);
|
||||
assert.equal(result, body, "non-observed path must return the exact same reference");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user