UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

589 lines (588 loc) 25.4 kB
import { C as parseStrictNonNegativeInteger, c as asFiniteNumberInRange, m as clampTimerTimeoutMs, x as parseStrictFiniteNumber } from "./number-coercion-CLj0HTDM.js"; import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js"; import { _ as parseCanonicalIpAddress, d as isLinkLocalIpAddress, h as isRfc8215LocalUseNat64Ipv6Address, o as isCloudMetadataIpAddress } from "./ip-BkT2Is0E.js"; import { a as containsSecretSentinel, c as resolveSecretSentinel, n as SECRET_SENTINEL_PATTERN, u as swapSecretSentinelsInText } from "./sentinel-ZgmfESkJ.js"; import { c as shouldUseEnvHttpProxyForUrl } from "./proxy-env-BepksPz2.js"; import { g as readResponseTextLimited, t as ProviderHttpError } from "./provider-http-errors-U-nhuk_f.js"; import { b as ssrfPolicyFromHttpBaseUrlAllowedOrigin, m as mergeSsrFPolicies, t as SsrFBlockedError, x as ssrfPolicyFromHttpBaseUrlFakeIpHostnameAllowlist } from "./ssrf-0QyXWOVG.js"; import { i as fetchWithSsrFGuard, s as withTrustedEnvProxyGuardedFetchMode } from "./fetch-guard-BMdGQhbb.js"; import { t as wrapGuardedBodyStream } from "./guarded-body-stream-38Py3kmx.js"; import { a as getModelProviderRequestRouteFacts, c as mergeModelProviderRequestOverrides, f as resolveProviderRequestPolicyConfig, i as buildProviderRequestDispatcherPolicy, o as getModelProviderRequestTransport } from "./provider-request-config-DIOYidiO.js"; import { r as resolveDebugProxySettings } from "./env-D-95VwzY.js"; import { r as ensureModelProviderLocalService } from "./provider-local-service-D9Gl9HJT.js"; import { n as getProviderTransportDispatcherPool } from "./provider-transport-dispatcher-pool-ByEUx_WY.js"; import { emitModelTransportDebug, formatModelTransportDebugUrl } from "@openclaw/ai/transports"; import { parseRetryAfterHttpDateMs } from "@openclaw/ai/internal/retry-after"; //#region src/agents/provider-transport-fetch.ts /** * Guarded provider fetch transport utilities. * * Applies request timeouts, proxy/TLS overrides, SSRF policy, local-service leases, retry hints, and SSE normalization. */ const DEFAULT_MAX_SDK_RETRY_WAIT_SECONDS = 60; const OPENAI_SDK_STREAM_CONTENT_SNIFF_BYTES = 2048; const log = createSubsystemLogger("provider-transport-fetch"); /** Max bytes for an entire JSON body synthesized into SSE frames. Prevents OOM * when a hostile streaming endpoint returns a never-ending JSON response * without Content-Length. */ const SSE_SYNTHESIZE_JSON_MAX_BYTES = 16777216; /** Max bytes read from a non-OK response body before truncation. */ const SSE_NONOK_BODY_MAX_BYTES = 65536; /** Max decoded characters buffered while waiting for the next SSE event boundary. */ const SSE_SANITIZE_BUFFER_MAX_CHARS = 16777216; const BLOCKED_EXACT_ORIGIN_TRUST_HOSTNAME_LABELS = /* @__PURE__ */ new Set(["instance-data"]); const PLAIN_DECIMAL_NUMBER_RE = /^\d+(?:\.\d+)?$/; function hasReadableSseData(block) { const dataLines = block.split(/\r\n|\n|\r/).filter((line) => line === "data" || line.startsWith("data:")).map((line) => { if (line === "data") return ""; const value = line.slice(5); return value.startsWith(" ") ? value.slice(1) : value; }); return dataLines.length > 0 && dataLines.join("\n").trim().length > 0; } function findSseEventBoundary(buffer) { let best; for (const delimiter of [ "\r\n\r\n", "\n\n", "\r\r" ]) { const index = buffer.indexOf(delimiter); if (index === -1) continue; if (!best || index < best.index) best = { index, length: delimiter.length }; } return best; } async function cancelReaderBestEffort(reader, reason) { await reader?.cancel(reason).catch(() => void 0); } function capNonOkResponseBodyLazily(response, maxBytes) { const source = response.body; if (!source) return response; let reader; let total = 0; const capped = new ReadableStream({ start() { reader = source.getReader(); }, async pull(controller) { try { const chunk = await reader?.read(); if (!chunk || chunk.done) { controller.close(); return; } const remaining = maxBytes - total; if (chunk.value.byteLength > remaining) { if (remaining > 0) controller.enqueue(chunk.value.subarray(0, remaining)); total = maxBytes; controller.close(); cancelReaderBestEffort(reader); return; } total += chunk.value.byteLength; controller.enqueue(chunk.value); } catch (error) { controller.error(error); cancelReaderBestEffort(reader, error); } }, async cancel(reason) { await cancelReaderBestEffort(reader, reason); } }); return new Response(capped, response); } function sanitizeOpenAISdkSseResponse(response, options) { const contentType = response.headers.get("content-type") ?? ""; if (!response.body) return response; if (!response.ok) return capNonOkResponseBodyLazily(response, SSE_NONOK_BODY_MAX_BYTES); if (options?.synthesizeJsonAsSse === true && (/\bapplication\/json\b/i.test(contentType) || /\+json\b/i.test(contentType))) { const source = response.body; const decoder = new TextDecoder(); const encoder = new TextEncoder(); let reader; let buffer = ""; let totalBytes = 0; const sseBody = new ReadableStream({ start() { reader = source.getReader(); }, async pull(controller) { try { for (;;) { const chunk = await reader?.read(); if (!chunk || chunk.done) { buffer += decoder.decode(); const data = buffer.trim(); if (data) controller.enqueue(encoder.encode(`data: ${data}\n\n`)); controller.enqueue(encoder.encode("data: [DONE]\n\n")); controller.close(); return; } const nextTotalBytes = totalBytes + chunk.value.byteLength; if (nextTotalBytes > SSE_SYNTHESIZE_JSON_MAX_BYTES) throw new Error(`Streaming JSON body exceeded ${SSE_SYNTHESIZE_JSON_MAX_BYTES} bytes while synthesizing SSE frames`); totalBytes = nextTotalBytes; buffer += decoder.decode(chunk.value, { stream: true }); } } catch (error) { await cancelReaderBestEffort(reader, error); controller.error(error); } }, async cancel(reason) { await cancelReaderBestEffort(reader, reason); } }); const headers = new Headers(response.headers); headers.set("content-type", "text/event-stream; charset=utf-8"); return new Response(sseBody, { status: response.status, statusText: response.statusText, headers }); } if (!/\btext\/event-stream\b/i.test(contentType)) return response; const source = response.body; const decoder = new TextDecoder(); const encoder = new TextEncoder(); let reader; let buffer = ""; const enqueueSanitized = (controller, text) => { let enqueued = 0; buffer += text; for (;;) { const boundary = findSseEventBoundary(buffer); if (!boundary) { if (buffer.length > SSE_SANITIZE_BUFFER_MAX_CHARS) throw new Error(`SSE response exceeded max buffer size (${SSE_SANITIZE_BUFFER_MAX_CHARS} chars) without event boundary`); return enqueued; } const block = buffer.slice(0, boundary.index); const separator = buffer.slice(boundary.index, boundary.index + boundary.length); buffer = buffer.slice(boundary.index + boundary.length); if (hasReadableSseData(block)) { controller.enqueue(encoder.encode(`${block}${separator}`)); enqueued += 1; return enqueued; } } }; const sanitizedBody = new ReadableStream({ start() { reader = source.getReader(); }, async pull(controller) { try { for (;;) { if (enqueueSanitized(controller, "") > 0) return; const chunk = await reader?.read(); if (!chunk || chunk.done) { const tail = decoder.decode(); if (tail) enqueueSanitized(controller, tail); if (buffer && hasReadableSseData(buffer)) controller.enqueue(encoder.encode(buffer)); buffer = ""; controller.close(); return; } if (enqueueSanitized(controller, decoder.decode(chunk.value, { stream: true })) > 0) return; } } catch (error) { await cancelReaderBestEffort(reader, error); controller.error(error); } }, async cancel(reason) { await cancelReaderBestEffort(reader, reason); } }); return new Response(sanitizedBody, { status: response.status, statusText: response.statusText, headers: response.headers }); } function shouldSanitizeOpenAISdkSseResponse(model) { if (model.provider !== "openai") return true; try { return new URL(model.baseUrl).hostname.toLowerCase() !== "api.openai.com"; } catch { return true; } } function isJsonContentType(contentType) { return /\bapplication\/json\b/i.test(contentType) || /\+json\b/i.test(contentType); } function classifyOpenAISdkStreamBodyPrefix(text) { const trimmed = text.replace(/^\uFEFF/u, "").trimStart(); if (!trimmed) return "unknown"; if (trimmed.startsWith("<")) return "html"; if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json"; if (/^(?::|(?:data|event|id|retry)(?::|\r?\n|\r))/u.test(trimmed)) return "sse"; const boundary = findSseEventBoundary(text); if (boundary && hasReadableSseData(text.slice(0, boundary.index))) return "sse"; return "unknown"; } async function classifyOpenAISdkStreamBody(response) { const reader = response.clone().body?.getReader(); if (!reader) return "unknown"; const decoder = new TextDecoder(); let total = 0; let text = ""; try { while (total < OPENAI_SDK_STREAM_CONTENT_SNIFF_BYTES) { const { value, done } = await reader.read(); if (done) break; if (!value || value.byteLength === 0) continue; const remaining = OPENAI_SDK_STREAM_CONTENT_SNIFF_BYTES - total; const chunk = value.byteLength > remaining ? value.subarray(0, remaining) : value; total += chunk.byteLength; text += decoder.decode(chunk, { stream: true }); const kind = classifyOpenAISdkStreamBodyPrefix(text); if (kind !== "unknown") return kind; } text += decoder.decode(); return classifyOpenAISdkStreamBodyPrefix(text); } finally { cancelReaderBestEffort(reader); } } function withOpenAISdkStreamContentType(response, contentType) { const headers = new Headers(response.headers); headers.set("content-type", contentType); return new Response(response.body, { status: response.status, statusText: response.statusText, headers }); } async function normalizeOpenAISdkStreamContentType(params) { const contentType = params.response.headers.get("content-type") ?? ""; if (!params.response.ok || !params.response.body) return params.response; if (/\btext\/event-stream\b/i.test(contentType)) return params.response; if (isJsonContentType(contentType)) { if (await classifyOpenAISdkStreamBody(params.response).catch(() => "unknown") === "sse") return withOpenAISdkStreamContentType(params.response, "text/event-stream; charset=utf-8"); return params.response; } if (!contentType.trim()) { const kind = await classifyOpenAISdkStreamBody(params.response).catch(() => "unknown"); if (kind === "sse") return withOpenAISdkStreamContentType(params.response, "text/event-stream; charset=utf-8"); if (kind === "json") return withOpenAISdkStreamContentType(params.response, "application/json; charset=utf-8"); } const body = await readResponseTextLimited(params.response).catch(() => ""); await params.release().catch(() => void 0); params.localServiceLease?.release(); const hint = `OpenAI-compatible streamed responses must be text/event-stream or JSON; got ${contentType || "missing content-type"}. Check the provider baseUrl; OpenAI-compatible APIs commonly require a /v1 path prefix.`; throw new ProviderHttpError(`${params.model.provider}/${params.model.id}: ${hint}`, { status: params.response.status, code: "invalid_provider_content_type", type: "invalid_response", body }); } function requestBodyHasStreamTrue(request, init) { const method = request?.method ?? init?.method; if (method && method.toUpperCase() !== "POST") return false; const contentType = (request?.headers ?? new Headers(init?.headers)).get("content-type") ?? ""; if (contentType && !/\bapplication\/json\b/i.test(contentType)) return false; let text; if (typeof init?.body === "string") text = init.body; if (!text) return false; try { return JSON.parse(text).stream === true; } catch { return false; } } function parseRetryAfterSeconds(headers) { const retryAfterMs = headers.get("retry-after-ms"); if (retryAfterMs) { const trimmedRetryAfterMs = retryAfterMs.trim(); if (/^\d+(?:\.\d+)?$/.test(trimmedRetryAfterMs)) { const milliseconds = asFiniteNumberInRange(parseStrictFiniteNumber(trimmedRetryAfterMs), { min: 0, max: Number.MAX_SAFE_INTEGER }); return milliseconds === void 0 ? Number.POSITIVE_INFINITY : milliseconds / 1e3; } } const retryAfter = headers.get("retry-after"); if (!retryAfter) return; const trimmedRetryAfterSeconds = retryAfter.trim(); if (/^\d+$/.test(trimmedRetryAfterSeconds)) return parseStrictNonNegativeInteger(trimmedRetryAfterSeconds) ?? Number.POSITIVE_INFINITY; const retryAt = parseRetryAfterHttpDateMs(trimmedRetryAfterSeconds); if (retryAt === void 0) return; return Math.max(0, (retryAt - Date.now()) / 1e3); } function resolveMaxSdkRetryWaitSeconds() { const raw = process.env.OPENCLAW_SDK_RETRY_MAX_WAIT_SECONDS?.trim(); if (!raw) return DEFAULT_MAX_SDK_RETRY_WAIT_SECONDS; if (/^(?:0|false|off|none|disabled)$/i.test(raw)) return; if (!PLAIN_DECIMAL_NUMBER_RE.test(raw)) return DEFAULT_MAX_SDK_RETRY_WAIT_SECONDS; const seconds = asFiniteNumberInRange(parseStrictFiniteNumber(raw), { min: 0, minExclusive: true, max: Number.MAX_SAFE_INTEGER }); if (seconds !== void 0) return seconds; return DEFAULT_MAX_SDK_RETRY_WAIT_SECONDS; } function shouldBypassLongSdkRetry(response) { const maxWaitSeconds = resolveMaxSdkRetryWaitSeconds(); if (maxWaitSeconds === void 0) return false; const status = response.status; if (!(status === 408 || status === 409 || status === 429 || status >= 500)) return false; const retryAfterSeconds = parseRetryAfterSeconds(response.headers); if (retryAfterSeconds !== void 0) return retryAfterSeconds > maxWaitSeconds; return status === 429; } function buildManagedResponse(response, release, refreshTimeout, localServiceLease) { const finalizeLocalServiceLease = () => { localServiceLease?.release(); }; if (!response.body) { release().finally(finalizeLocalServiceLease); return response; } const wrappedBody = wrapGuardedBodyStream({ body: response.body, cleanup: async () => { try { await release().catch(() => void 0); } finally { finalizeLocalServiceLease(); } }, refreshTimeout }); return new Response(wrappedBody, { status: response.status, statusText: response.statusText, headers: response.headers }); } function resolveModelRequestPolicy(model) { const debugProxy = resolveDebugProxySettings(); let explicitDebugProxyUrl; if (debugProxy.enabled && debugProxy.proxyUrl) try { if (new URL(model.baseUrl).protocol === "https:") explicitDebugProxyUrl = debugProxy.proxyUrl; } catch {} const request = mergeModelProviderRequestOverrides(getModelProviderRequestTransport(model), { proxy: explicitDebugProxyUrl ? { mode: "explicit-proxy", url: explicitDebugProxyUrl } : void 0 }); const routeFacts = getModelProviderRequestRouteFacts(model); return resolveProviderRequestPolicyConfig({ provider: model.provider, api: model.api, baseUrl: model.baseUrl, ...routeFacts ? { routeFacts } : {}, capability: "llm", transport: "stream", request }); } function resolveModelRequestTimeoutMs(model, timeoutMs) { if (timeoutMs !== void 0) return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? clampTimerTimeoutMs(timeoutMs) : void 0; const modelTimeoutMs = model.requestTimeoutMs; return typeof modelTimeoutMs === "number" && Number.isFinite(modelTimeoutMs) && modelTimeoutMs > 0 ? clampTimerTimeoutMs(modelTimeoutMs) : void 0; } function buildModelRequestSignal(baseSignal, timeoutMs) { if (timeoutMs === void 0) return baseSignal; const timeoutSignal = AbortSignal.timeout(timeoutMs); if (!baseSignal) return timeoutSignal; return AbortSignal.any([baseSignal, timeoutSignal]); } function resolveHttpOrigin(value) { if (typeof value !== "string" || !value.trim()) return; try { const parsed = new URL(value); if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return; parsed.hostname = parsed.hostname.replace(/\.+$/, ""); return parsed.origin.toLowerCase(); } catch { return; } } function normalizeProviderOriginHostname(value) { if (typeof value !== "string" || !value.trim()) return; try { const parsed = new URL(value); if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return; return parsed.hostname.trim().toLowerCase().replace(/\.+$/, "") || void 0; } catch { return; } } function canImplicitlyTrustConfiguredBaseUrlOrigin(value) { const hostname = normalizeProviderOriginHostname(value); if (!hostname) return false; return !hostname.split(".").filter(Boolean).some((label) => label.includes("metadata") || BLOCKED_EXACT_ORIGIN_TRUST_HOSTNAME_LABELS.has(label)) && !isLinkLocalIpAddress(hostname) && !isCloudMetadataIpAddress(hostname) && !isRfc8215LocalUseNat64Ipv6Address(hostname); } function canApplyFakeIpHostnamePolicy(value) { const hostname = normalizeProviderOriginHostname(value); if (!hostname) return false; return !hostname.split(".").filter(Boolean).some((label) => label.includes("metadata") || BLOCKED_EXACT_ORIGIN_TRUST_HOSTNAME_LABELS.has(label)) && !parseCanonicalIpAddress(hostname); } function resolveProviderTransportSsrFPolicy(params) { const baseUrl = params.baseUrl; const baseOrigin = resolveHttpOrigin(baseUrl); const requestOrigin = resolveHttpOrigin(params.url); const requestMatchesBaseOrigin = typeof baseUrl === "string" && Boolean(baseOrigin) && requestOrigin === baseOrigin; const baseUrlOriginPolicy = requestMatchesBaseOrigin && params.trustConfiguredBaseUrlOrigin && canImplicitlyTrustConfiguredBaseUrlOrigin(baseUrl) ? ssrfPolicyFromHttpBaseUrlAllowedOrigin(baseUrl) : void 0; const fakeIpPolicy = requestMatchesBaseOrigin && canApplyFakeIpHostnamePolicy(baseUrl) ? ssrfPolicyFromHttpBaseUrlFakeIpHostnameAllowlist(baseUrl) : void 0; return mergeSsrFPolicies(baseUrlOriginPolicy, fakeIpPolicy, params.allowPrivateNetwork ? { allowPrivateNetwork: true } : void 0); } function withModelProviderNetworkRemediation(error, params) { const baseOrigin = resolveHttpOrigin(params.baseUrl); const requestOrigin = resolveHttpOrigin(params.url); const hostname = normalizeProviderOriginHostname(params.baseUrl); if (!(error instanceof SsrFBlockedError) || !baseOrigin || requestOrigin !== baseOrigin || !hostname || !isRfc8215LocalUseNat64Ipv6Address(hostname)) return error; return new SsrFBlockedError(`Configured model provider ${params.providerId} uses local-use NAT64 origin ${baseOrigin}, which OpenClaw blocks by default. Move the provider to a loopback, LAN, or tailnet address, or set models.providers.${params.providerId}.request.allowPrivateNetwork=true only for an operator-controlled endpoint. Original block: ${error.message}`); } function headersContainSecretSentinel(headers) { if (!headers) return false; for (const value of new Headers(headers).values()) if (containsSecretSentinel(value)) return true; return false; } function swapSecretSentinelsInUrl(url) { if (!containsSecretSentinel(url)) return { text: url, unknown: [] }; const unknown = /* @__PURE__ */ new Set(); return { text: url.replace(new RegExp(SECRET_SENTINEL_PATTERN.source, "g"), (sentinel) => { const value = resolveSecretSentinel(sentinel); if (value === void 0) { unknown.add(sentinel); return sentinel; } return encodeURIComponent(value); }), unknown: [...unknown] }; } function swapSecretSentinelsForEgress(params) { if (!containsSecretSentinel(params.url) && !headersContainSecretSentinel(params.headers)) return { url: params.url }; const urlSwap = swapSecretSentinelsInUrl(params.url); const headers = params.headers ? new Headers(params.headers) : void 0; const unknown = new Set(urlSwap.unknown); if (headers) for (const [name, value] of headers.entries()) { const swapped = swapSecretSentinelsInText(value); headers.set(name, swapped.text); for (const sentinel of swapped.unknown) unknown.add(sentinel); } const unresolved = unknown.values().next().value; if (unresolved) throw new Error(`Secret sentinel ${unresolved} is not registered in this process; refusing to send request`); return { url: urlSwap.text, ...headers ? { headers } : {} }; } function buildGuardedModelFetch(model, timeoutMs, options) { const requestConfig = resolveModelRequestPolicy(model); const dispatcherPolicy = buildProviderRequestDispatcherPolicy(requestConfig); const requestTimeoutMs = resolveModelRequestTimeoutMs(model, timeoutMs); const summarizeError = (error) => { if (!error || typeof error !== "object") return `type=${typeof error}`; const record = error; const cause = record.cause && typeof record.cause === "object" ? record.cause : void 0; const read = (value) => typeof value === "string" ? value : typeof value; return [ `name=${read(record.name)}`, `code=${read(record.code)}`, `causeName=${read(cause?.name)}`, `causeCode=${read(cause?.code)}`, `message=${error instanceof Error ? error.message : read(record.message)}` ].join(" "); }; return async (input, init) => { let localServiceLease; const request = input instanceof Request ? new Request(input, init) : void 0; const rawUrl = request?.url ?? (input instanceof URL ? input.toString() : typeof input === "string" ? input : (() => { throw new Error("Unsupported fetch input for transport-aware model request"); })()); const rawHeaders = request?.headers ?? init?.headers; const swappedEgress = swapSecretSentinelsForEgress({ url: rawUrl, headers: rawHeaders }); const url = swappedEgress.url; const policy = resolveProviderTransportSsrFPolicy({ baseUrl: model.baseUrl, url, allowPrivateNetwork: requestConfig.allowPrivateNetwork, trustConfiguredBaseUrlOrigin: requestConfig.trustConfiguredBaseUrlOrigin }); const baseInit = (request && { method: request.method, headers: swappedEgress.headers ?? request.headers, body: request.body ?? void 0, redirect: request.redirect, signal: request.signal, ...request.body ? { duplex: "half" } : {} }) ?? (swappedEgress.headers && init ? { ...init, headers: swappedEgress.headers } : init); const baseSignal = baseInit?.signal ?? void 0; const localServiceSignal = buildModelRequestSignal(baseSignal, requestTimeoutMs); const guardedFetchOptions = { url, init: baseInit, capture: { meta: { provider: model.provider, api: model.api, model: model.id } }, dispatcherPolicy, dispatcherPool: getProviderTransportDispatcherPool(), timeoutMs: requestTimeoutMs, ...baseSignal ? { signal: baseSignal } : {}, allowCrossOriginUnsafeRedirectReplay: false, ...policy ? { policy } : {} }; let result; const fetchStartedAt = Date.now(); const useEnvProxy = !dispatcherPolicy && shouldUseEnvHttpProxyForUrl(url); emitModelTransportDebug(log, `[model-fetch] start provider=${model.provider} api=${model.api} model=${model.id} method=${baseInit?.method ?? "GET"} url=${formatModelTransportDebugUrl(rawUrl)} timeoutMs=${requestTimeoutMs} proxy=${dispatcherPolicy ? "configured" : useEnvProxy ? "env" : "none"} policy=${policy ? "custom" : "default"}`); try { localServiceLease = await ensureModelProviderLocalService(model, rawHeaders, localServiceSignal); result = await fetchWithSsrFGuard(useEnvProxy ? withTrustedEnvProxyGuardedFetchMode(guardedFetchOptions) : guardedFetchOptions); } catch (error) { const remediatedError = withModelProviderNetworkRemediation(error, { baseUrl: model.baseUrl, providerId: model.provider, url }); log.warn(`[model-fetch] error provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - fetchStartedAt} ${summarizeError(remediatedError)}`); localServiceLease?.release(); throw remediatedError; } let response = result.response; emitModelTransportDebug(log, `[model-fetch] response provider=${model.provider} api=${model.api} model=${model.id} status=${response.status} elapsedMs=${Date.now() - fetchStartedAt} dispatcher=${result.dispatcherReused ? "reused" : "new"} contentType=${response.headers.get("content-type") ?? ""}`); if (shouldBypassLongSdkRetry(response)) { const headers = new Headers(response.headers); headers.set("x-should-retry", "false"); response = new Response(response.body, { status: response.status, statusText: response.statusText, headers }); } const synthesizeJsonAsSse = options?.sanitizeSse !== false && !/\btext\/event-stream\b/i.test(response.headers.get("content-type") ?? "") && requestBodyHasStreamTrue(request, baseInit); if (synthesizeJsonAsSse) response = await normalizeOpenAISdkStreamContentType({ response, model, release: result.release, localServiceLease }); response = buildManagedResponse(response, result.release, result.refreshTimeout, localServiceLease); return options?.sanitizeSse === false || !shouldSanitizeOpenAISdkSseResponse(model) ? response : sanitizeOpenAISdkSseResponse(response, { synthesizeJsonAsSse }); }; } //#endregion export { resolveModelRequestTimeoutMs as n, resolveProviderTransportSsrFPolicy as r, buildGuardedModelFetch as t };