text/image content. RouteShift decodes each PDF, enforces per-file and per-request byte, page, and text-length budgets, and replaces the raw file with either extracted text or a validated native-PDF part before dispatching upstream.
Like web search, file parsing runs entirely proxy-side. You don’t need to swap providers or bolt on a document-extraction service — any model you can call through RouteShift can accept a PDF.
When to use it
Turn on file parsing when a user turn (or system prompt) needs to reference the contents of a PDF — a report the user uploaded, a spec pulled from a URL, a receipt, a contract. The plugin is a good fit for:- Client apps that already send OpenAI-style
input_fileparts and want the same shape to work across every provider RouteShift routes to. - Workflows that hand the model a URL to a PDF instead of pre-extracting text.
- Multi-provider setups where you want fail-closed passthrough on Anthropic and Gemini (native PDF understanding) with automatic text fallback everywhere else.
Opt in per request
The file parser is triggered in one of three ways:- A
fileorinput_filecontent part on any user message. The plugin runs automatically as soon as a request contains one — nopluginsarray entry is required. - An explicit
pluginsentry withid: "file-parser". Use this when you want to mark the plugin asrequired(so a parse failure short-circuits the request instead of degrading), or to force text extraction on a provider that would otherwise get native PDF passthrough. - A
filepart inside thesystem_prompt. System documents are always extracted to text, regardless of the target provider.
plugins array is stripped from the payload before dispatch — upstream providers never see it.
Base64 inline PDF
URL-backed PDF
Explicit plugin entry (mark as required, or force text extraction)
file-parser entry also opts out of native PDF passthrough — every file goes through the text-extraction path even when the target provider natively supports PDFs. Use this when you want consistent, provider-agnostic text output.
Supported inputs
The plugin accepts eitherfile or input_file content parts. The PDF payload can be provided as:
A
filename (or name) may be supplied at the top level or nested under file; RouteShift sanitizes it before forwarding — non-alphanumeric characters become underscores, and the length is capped. If no filename is supplied, document.pdf is used.
Only PDFs (application/pdf with a %PDF- magic header) are accepted. Anything else surfaces an unsupported_file_type warning.
Native PDF passthrough vs. text extraction
RouteShift picks between two output shapes for each user-message file part:- Native PDF passthrough. When the resolved target model — and every model in the fallback chain — is explicitly marked as supporting native PDF (currently Claude and Gemini families), the plugin forwards a bounded, validated PDF part directly to the provider. This preserves layout, tables, and figures that text extraction would lose.
- Text extraction. In every other case, the plugin extracts text with a page-count cap and replaces the file part with a
textpart of the form[Extracted from <filename>]\n<extracted text>.
file-parser plugin entry (see above) forces text extraction as well.
Budgets and limits
Every request runs against a set of per-file and per-request budgets. All of them are configurable per-deployment; the defaults are:
Every fetch error conservatively exhausts the request’s remaining byte budget: a failed request can already have read a partial body, and the plugin can’t know how much. That’s deliberate — it makes each file attempt a hard, all-or-nothing charge against the aggregate cap.
Optional vs. required plugins
The file parser is optional by default. When a file can’t be parsed — the URL was blocked, the payload was too large, the content type wasn’t PDF, extraction failed — the request continues without the file, and aPluginWarning is attached to the response and the request log.
Set "required": true when the model shouldn’t answer without the file. A required-plugin failure short-circuits the request with:
Warning codes
Each warning surfaces as{ plugin: "file-parser", code: "<code>" } on the response and is persisted in the request log’s plugin-run audit row. Codes are stable and deliberately do not include filenames, URLs, or response bodies.
Security
URL-backed PDFs are fetched through RouteShift’s SSRF-safe fetch path: every candidate address is validated and DNS-pinned before transport, so a URL cannot rebind to a private address between the check and the connection. Loopback, IPv4 private/special-use ranges, ULA, link-local and site-local IPv6, multicast, discard-only, IPv4-embedded IPv6, and NAT64 forms are all rejected. Redirects are followed up to three hops and re-validated at each hop. Filenames are sanitized before they reach an upstream provider. Extracted text is wrapped with a clearly labeled[Extracted from <filename>] header so the model treats the content as supplied context, not authoritative instructions.
Billing and audit
Every plugin execution — file parser included — writes a sanitized audit row that captures the plugin id, status (ok / warning / error / skipped), a stable detail code, cost, and latency. Rows are keyed by (request_id, plugin_id) and are visible in your observability feed alongside the underlying request.
The file parser itself does not add a per-request surcharge — the cost of extraction is folded into the request. Plugin surcharges (currently applied by web search) are tracked separately from provider model cost so plugin fees never get relabeled as routing spend. See Savings and pricing for how the two components appear in usage reports.