From ab60b95e317082d5fa0833f7e69f81d69c29e264 Mon Sep 17 00:00:00 2001 From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Date: Thu, 27 Aug 2026 03:07:06 -0300 Subject: [PATCH] fix(video): redact executor diagnostics --- open-sse/executors/base.ts | 4 +++ open-sse/executors/glm.ts | 11 ++++++-- open-sse/handlers/chatCore.ts | 3 +++ open-sse/handlers/chatCore/executorProxy.ts | 28 +++++++++++---------- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/open-sse/executors/base.ts b/open-sse/executors/base.ts index 6dc63fc72f..ff4489beb3 100644 --- a/open-sse/executors/base.ts +++ b/open-sse/executors/base.ts @@ -202,6 +202,10 @@ export type ExecuteInput = { * this to apply client-format-aware policies such as `` close-marker * suppression. */ clientResponseFormat?: string | null; + /** True when upstream diagnostics may echo a video transcript. Executors must + * preserve operational responses/errors while omitting those echoes from + * retained logs. */ + videoTranscriptSensitive?: boolean; /** Callback to persist tokens that are proactively refreshed during execution. * Accepts a partial credentials patch (e.g. `{ accessToken, refreshToken }` or * `{ testStatus: "expired", isActive: false }`); the caller merges into the diff --git a/open-sse/executors/glm.ts b/open-sse/executors/glm.ts index 194c8a952b..3cc21f4477 100644 --- a/open-sse/executors/glm.ts +++ b/open-sse/executors/glm.ts @@ -1,4 +1,5 @@ import { randomUUID } from "node:crypto"; +import { redactVideoTranscriptSensitiveText } from "../../src/lib/guardrails/videoTranscriptLogRedaction.ts"; import type { KeyHealth } from "../services/apiKeyRotator.ts"; import { DefaultExecutor } from "./default.ts"; @@ -45,6 +46,11 @@ function asRecord(value: unknown): JsonRecord | null { return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : null; } +function retainGlmDiagnostic(error: unknown, input: ExecuteInput): string { + const message = error instanceof Error ? error.message : String(error); + return redactVideoTranscriptSensitiveText(message, input.videoTranscriptSensitive === true); +} + function getEffectiveKey(credentials: ProviderCredentials): string { const extraKeys = (credentials.providerSpecificData?.extraApiKeys as string[] | undefined) ?? []; if (credentials.apiKey && credentials.connectionId && extraKeys.length > 0) { @@ -465,6 +471,7 @@ export class GlmExecutor extends DefaultExecutor { timeoutMs: STREAM_READINESS_TIMEOUT_MS, provider: this.provider, model: input.model, + redactUpstreamDiagnosticForLog: input.videoTranscriptSensitive === true, log: input.log, }); response = readiness.response; @@ -559,7 +566,7 @@ export class GlmExecutor extends DefaultExecutor { if (!isRetryableGlmFallbackError(error)) throw error; input.log?.debug?.( "GLM_FALLBACK", - `${primaryTransport} error (${error instanceof Error ? error.message : String(error)}); trying ${fallbackTransport}` + `${primaryTransport} error (${retainGlmDiagnostic(error, input)}); trying ${fallbackTransport}` ); } @@ -572,7 +579,7 @@ export class GlmExecutor extends DefaultExecutor { if (!primaryResult) throw error; input.log?.debug?.( "GLM_FALLBACK", - `${fallbackTransport} fallback failed (${error instanceof Error ? error.message : String(error)}); returning primary response` + `${fallbackTransport} fallback failed (${retainGlmDiagnostic(error, input)}); returning primary response` ); } diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index 54a2139de7..0c2ec5bb2b 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -3173,6 +3173,7 @@ export async function handleChatCore({ userAgent ), clientResponseFormat, + videoTranscriptSensitive, onCredentialsRefreshed, skipUpstreamRetry, contextEditing: { enabled: contextEditingEnabled }, @@ -3489,6 +3490,7 @@ export async function handleChatCore({ userAgent ), clientResponseFormat, + videoTranscriptSensitive, onCredentialsRefreshed, skipUpstreamRetry, contextEditing: { enabled: contextEditingEnabled }, @@ -4036,6 +4038,7 @@ export async function handleChatCore({ upstreamExtraHeaders: buildUpstreamHeadersForExecute(retryModelId), clientHeaders: buildExecutorClientHeaders(clientRawRequest?.headers, userAgent), clientResponseFormat, + videoTranscriptSensitive, onCredentialsRefreshed, skipUpstreamRetry: isCombo, contextEditing: { enabled: contextEditingEnabled }, diff --git a/open-sse/handlers/chatCore/executorProxy.ts b/open-sse/handlers/chatCore/executorProxy.ts index bb0a3b0753..5421c7bf10 100644 --- a/open-sse/handlers/chatCore/executorProxy.ts +++ b/open-sse/handlers/chatCore/executorProxy.ts @@ -17,8 +17,10 @@ */ import { getExecutor } from "../../executors/index.ts"; +import type { ExecuteInput } from "../../executors/base.ts"; import { isCliproxyapiDeepModeEnabled } from "../../executors/cliproxyapi.ts"; import { isDarioDeepModeEnabled } from "../../executors/dario.ts"; +import { redactVideoTranscriptSensitiveText } from "../../../src/lib/guardrails/videoTranscriptLogRedaction.ts"; import { getCachedSettings } from "@/lib/db/readCache"; import { getUpstreamProxyConfigCached } from "./comboContextCache.ts"; import type { FallbackBackend } from "@/lib/db/upstreamProxy"; @@ -48,6 +50,11 @@ function parseFallbackCodes(raw: unknown): number[] | null { return parsed.length > 0 ? parsed : null; } +function retainExecutorDiagnostic(error: unknown, input: ExecuteInput): string { + const message = error instanceof Error ? error.message : String(error); + return redactVideoTranscriptSensitiveText(message, input.videoTranscriptSensitive === true); +} + /** * Reads the CLIProxyAPI-related settings shared by both the direct * `mode: "cliproxyapi"` passthrough leg and the `mode: "fallback"` retry leg: @@ -155,25 +162,20 @@ export async function resolveExecutorWithProxy( const isRetryableStatus = (s: number) => fallbackCodes.includes(s) || s === 0; const wrapper = Object.create(nativeExec); - wrapper.execute = async (input: { - model: string; - body: unknown; - stream: boolean; - credentials: unknown; - signal?: AbortSignal | null; - log?: unknown; - upstreamExtraHeaders?: Record | null; - }) => { + wrapper.execute = async (input: ExecuteInput) => { let result; try { result = await nativeExec.execute(input); } catch (err) { - const errMsg = err instanceof Error ? err.message : String(err); - log?.info?.("UPSTREAM_PROXY", `${prov} native error (${errMsg}), retrying via ${backendLabel}`); + const errMsg = retainExecutorDiagnostic(err, input); + log?.info?.( + "UPSTREAM_PROXY", + `${prov} native error (${errMsg}), retrying via ${backendLabel}` + ); try { return await proxyExec.execute(input); } catch (proxyErr) { - const proxyMsg = proxyErr instanceof Error ? proxyErr.message : String(proxyErr); + const proxyMsg = retainExecutorDiagnostic(proxyErr, input); log?.error?.("UPSTREAM_PROXY", `${prov} ${backendLabel} fallback also failed: ${proxyMsg}`); throw proxyErr; } @@ -189,7 +191,7 @@ export async function resolveExecutorWithProxy( try { return await proxyExec.execute(input); } catch (proxyErr) { - const proxyMsg = proxyErr instanceof Error ? proxyErr.message : String(proxyErr); + const proxyMsg = retainExecutorDiagnostic(proxyErr, input); log?.error?.("UPSTREAM_PROXY", `${prov} ${backendLabel} fallback also failed: ${proxyMsg}`); throw proxyErr; }