From 37c97964af2eef51034d5da32a1e5f3e2d61ea7b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 17 Feb 2026 00:29:22 +0000 Subject: [PATCH] refactor(media): centralize input file limit resolution --- src/gateway/openresponses-http.ts | 20 +++----------------- src/media-understanding/apply.ts | 24 +++--------------------- src/media/input-files.ts | 30 ++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/gateway/openresponses-http.ts b/src/gateway/openresponses-http.ts index 9e05de501a..3fe440d4c3 100644 --- a/src/gateway/openresponses-http.ts +++ b/src/gateway/openresponses-http.ts @@ -16,19 +16,14 @@ import type { GatewayHttpResponsesConfig } from "../config/types.gateway.js"; import { emitAgentEvent, onAgentEvent } from "../infra/agent-events.js"; import { logWarn } from "../logger.js"; import { - DEFAULT_INPUT_FILE_MAX_BYTES, - DEFAULT_INPUT_FILE_MAX_CHARS, - DEFAULT_INPUT_FILE_MIMES, DEFAULT_INPUT_IMAGE_MAX_BYTES, DEFAULT_INPUT_IMAGE_MIMES, DEFAULT_INPUT_MAX_REDIRECTS, - DEFAULT_INPUT_PDF_MAX_PAGES, - DEFAULT_INPUT_PDF_MAX_PIXELS, - DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, DEFAULT_INPUT_TIMEOUT_MS, extractFileContentFromSource, extractImageContentFromSource, normalizeMimeList, + resolveInputFileLimits, type InputFileLimits, type InputImageLimits, type InputImageSource, @@ -109,6 +104,7 @@ function resolveResponsesLimits( ): ResolvedResponsesLimits { const files = config?.files; const images = config?.images; + const fileLimits = resolveInputFileLimits(files); return { maxBodyBytes: config?.maxBodyBytes ?? DEFAULT_BODY_BYTES, maxUrlParts: @@ -116,18 +112,8 @@ function resolveResponsesLimits( ? Math.max(0, Math.floor(config.maxUrlParts)) : DEFAULT_MAX_URL_PARTS, files: { - allowUrl: files?.allowUrl ?? true, + ...fileLimits, urlAllowlist: normalizeHostnameAllowlist(files?.urlAllowlist), - allowedMimes: normalizeMimeList(files?.allowedMimes, DEFAULT_INPUT_FILE_MIMES), - maxBytes: files?.maxBytes ?? DEFAULT_INPUT_FILE_MAX_BYTES, - maxChars: files?.maxChars ?? DEFAULT_INPUT_FILE_MAX_CHARS, - maxRedirects: files?.maxRedirects ?? DEFAULT_INPUT_MAX_REDIRECTS, - timeoutMs: files?.timeoutMs ?? DEFAULT_INPUT_TIMEOUT_MS, - pdf: { - maxPages: files?.pdf?.maxPages ?? DEFAULT_INPUT_PDF_MAX_PAGES, - maxPixels: files?.pdf?.maxPixels ?? DEFAULT_INPUT_PDF_MAX_PIXELS, - minTextChars: files?.pdf?.minTextChars ?? DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, - }, }, images: { allowUrl: images?.allowUrl ?? true, diff --git a/src/media-understanding/apply.ts b/src/media-understanding/apply.ts index 481b28efa6..6de29873bb 100644 --- a/src/media-understanding/apply.ts +++ b/src/media-understanding/apply.ts @@ -4,17 +4,9 @@ import type { MsgContext } from "../auto-reply/templating.js"; import type { OpenClawConfig } from "../config/config.js"; import { logVerbose, shouldLogVerbose } from "../globals.js"; import { - DEFAULT_INPUT_FILE_MAX_BYTES, - DEFAULT_INPUT_FILE_MAX_CHARS, - DEFAULT_INPUT_FILE_MIMES, - DEFAULT_INPUT_MAX_REDIRECTS, - DEFAULT_INPUT_PDF_MAX_PAGES, - DEFAULT_INPUT_PDF_MAX_PIXELS, - DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, - DEFAULT_INPUT_TIMEOUT_MS, extractFileContentFromSource, - normalizeMimeList, normalizeMimeType, + resolveInputFileLimits, } from "../media/input-files.js"; import { resolveAttachmentKind } from "./attachments.js"; import { runWithConcurrency } from "./concurrency.js"; @@ -107,20 +99,10 @@ function sanitizeMimeType(value?: string): string | undefined { function resolveFileLimits(cfg: OpenClawConfig) { const files = cfg.gateway?.http?.endpoints?.responses?.files; - const allowedMimesConfigured = Boolean(files?.allowedMimes && files.allowedMimes.length > 0); + const allowedMimesConfigured = Boolean(files?.allowedMimes?.length); return { - allowUrl: files?.allowUrl ?? true, - allowedMimes: normalizeMimeList(files?.allowedMimes, DEFAULT_INPUT_FILE_MIMES), + ...resolveInputFileLimits(files), allowedMimesConfigured, - maxBytes: files?.maxBytes ?? DEFAULT_INPUT_FILE_MAX_BYTES, - maxChars: files?.maxChars ?? DEFAULT_INPUT_FILE_MAX_CHARS, - maxRedirects: files?.maxRedirects ?? DEFAULT_INPUT_MAX_REDIRECTS, - timeoutMs: files?.timeoutMs ?? DEFAULT_INPUT_TIMEOUT_MS, - pdf: { - maxPages: files?.pdf?.maxPages ?? DEFAULT_INPUT_PDF_MAX_PAGES, - maxPixels: files?.pdf?.maxPixels ?? DEFAULT_INPUT_PDF_MAX_PIXELS, - minTextChars: files?.pdf?.minTextChars ?? DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, - }, }; } diff --git a/src/media/input-files.ts b/src/media/input-files.ts index 3d24db0d43..61fc067ef9 100644 --- a/src/media/input-files.ts +++ b/src/media/input-files.ts @@ -64,6 +64,20 @@ export type InputFileLimits = { pdf: InputPdfLimits; }; +export type InputFileLimitsConfig = { + allowUrl?: boolean; + allowedMimes?: string[]; + maxBytes?: number; + maxChars?: number; + maxRedirects?: number; + timeoutMs?: number; + pdf?: { + maxPages?: number; + maxPixels?: number; + minTextChars?: number; + }; +}; + export type InputImageLimits = { allowUrl: boolean; urlAllowlist?: string[]; @@ -154,6 +168,22 @@ export function normalizeMimeList(values: string[] | undefined, fallback: string return new Set(input.map((value) => normalizeMimeType(value)).filter(Boolean) as string[]); } +export function resolveInputFileLimits(config?: InputFileLimitsConfig): InputFileLimits { + return { + allowUrl: config?.allowUrl ?? true, + allowedMimes: normalizeMimeList(config?.allowedMimes, DEFAULT_INPUT_FILE_MIMES), + maxBytes: config?.maxBytes ?? DEFAULT_INPUT_FILE_MAX_BYTES, + maxChars: config?.maxChars ?? DEFAULT_INPUT_FILE_MAX_CHARS, + maxRedirects: config?.maxRedirects ?? DEFAULT_INPUT_MAX_REDIRECTS, + timeoutMs: config?.timeoutMs ?? DEFAULT_INPUT_TIMEOUT_MS, + pdf: { + maxPages: config?.pdf?.maxPages ?? DEFAULT_INPUT_PDF_MAX_PAGES, + maxPixels: config?.pdf?.maxPixels ?? DEFAULT_INPUT_PDF_MAX_PIXELS, + minTextChars: config?.pdf?.minTextChars ?? DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, + }, + }; +} + export async function fetchWithGuard(params: { url: string; maxBytes: number;