Skip to content

feat: support for OpenAI Batch API with single backend - #2883

Open
sivanantha321 wants to merge 34 commits into
theagentrouter:mainfrom
sivanantha321:openai-batch-api-single-backend
Open

sivanantha321 wants to merge 34 commits into
theagentrouter:mainfrom
sivanantha321:openai-batch-api-single-backend

Conversation

@sivanantha321

@sivanantha321 sivanantha321 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Description

This adds the OpenAI-compatible Batch API (/v1/batches) on top of the Files API, with the same
single-backend design. Clients can create, retrieve, list, and cancel batches through the gateway,
and download batch results through the existing Files API.

Like a file, a batch lives on one upstream backend: the backend that holds its input file. The
gateway keeps no state. It encrypts the owning backend and the routing model into each batch id it
returns, using the same codec and fileIDEncryptionSeed as file ids, so no new configuration is
needed.

Routing:

  • Create (POST /v1/batches) takes no model. It is routed by the backend and model recorded in
    the gateway input_file_id, which is replaced by the provider's own file id before forwarding.
  • Retrieve (GET /v1/batches/{id}) and cancel (POST /v1/batches/{id}/cancel) are routed by the
    model recorded in the batch id, and the path is rewritten to the provider's id.
  • List (GET /v1/batches) is routed by ?model=, or by the model in a gateway after cursor, so
    SDK auto-pagination works after the first page. model is not forwarded upstream.

Responses:

  • id, first_id, and last_id become gateway batch ids.
  • input_file_id, output_file_id, and error_file_id become gateway file ids for the same
    backend and model, so GET /v1/files/{output_file_id}/content works unchanged.
  • Ids are rewritten in place, so fields the gateway does not model (for example usage) pass
    through. Upstream error responses pass through unchanged. A successful response whose ids cannot
    be re-encoded becomes a 502 rather than leaking provider ids.

As with Files, each route rule must have exactly one backend, the id must belong to the backend
serving the request, and only the OpenAI schema has translators today.

Every request goes through the Batch translator in the upstream filter, as the Files processor
does, so a future provider translator can rewrite requests.

Related PRs

Special notes for reviewers (if applicable)

  • Out of scope in this PR: multi-backend routing, non-OpenAI translators, rewriting model inside the JSONL
    input file, and token-usage metering. Batch work runs asynchronously on the provider, so the
    gateway does not record token usage for it.

Add support for the OpenAI-compatible Files API (/v1/files upload, list,
retrieve, content, delete) without backend-sticky routing.

- Gateway-issued, encrypted file ids (internal/idcodec) encode the owning
  backend and the routing model, so follow-up requests route by the
  recorded model and ids stay stable if multi-backend routing is added.
- Files requests are rejected when the matched route rule has more than
  one backend, or when a file id belongs to a different backend than the
  one serving the route.
- Upload routes by the multipart model field; list routes by ?model= (or
  the model carried in the after id). Routing-only fields are stripped
  before forwarding upstream.
- Only the OpenAI schema is supported; other schemas fail closed.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…ngle-backend

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…ngle-backend

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Upstream errors and Envoy local replies (e.g. upstream connect errors)
carry no file object, so re-encoding them produced a misleading 502
"no backend route found". Return them to the client unchanged.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
File ids were encrypted with the MCP session crypto, which uses a random
salt and nonce per call, so the same file got a different id in every
response. Replace the AES-GCM codec and pluggable Crypto with a single
codec using deterministic AES-GCM (HMAC-derived synthetic nonce), keys
derived once at startup, and an optional fallback seed for rotation.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
- Drop unused nativeID and redundant uploadModel fields.
- Replace backendNamespace/backendName/backendKnown with a single
  backend *backendKey, set only by SetBackend so a known backend always
  has a resolved translator.
- Factor shared response re-encode prelude into translatedResponseBody.
- Add routeByModel, requestHeadersResponse and newRequestHeaderMutation
  helpers; originalPathHeader is now set in one place.
- Merge stripQueryParam and rewriteAfterParam into setQueryParam.

No client-visible behaviour change.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…ngle-backend

Brings in "extproc: redact sensitive information in logs from immediate
responses" (theagentrouter#2852).

Also tidies filesProcessor comments: the backend field doc no longer
claims a translator invariant, and "gateway ids" is reworded to
"file ids".

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…ngle-backend

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Add TestServer_ProcessorForPath_Prefix for RegisterPrefix and the prefix
fallback in processorForPath: prefix and nested paths, query stripping,
exact-over-prefix and longest-prefix precedence, segment-boundary
matching, and upstream lookup via the original path header.

Also drop the redundant internaljson import alias in
files_processor_test.go.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…Helm

Previously --fileIDEncryptionSeed was only defined on extproc, so every
deployment used the public default seed. The controller now accepts
fileIDEncryption seed/iteration/fallback flags and passes them to every
extproc sidecar (independent of MCP), including them in the drift hash.

The Helm chart generates a random seed in a <fullname>-file-id-encryption
Secret (reused on upgrade), supports existingSecret, and exposes a
fallback seed for rotation. Extproc warns when the default seed is used.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…d Files docs

Document the Helm controller.fileIDEncryption values, the forge risk of the
well-known default seed, seed stability/rotation, and the 502 re-encode error.
Remove stale multi-backend claims (stitched lists, fallback/load balancing)
and the incorrect 501 status from supported-endpoints.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Call translator.RequestBody at the upstream filter once the backend is known,
passing an OpenAI-shaped FilesRequest and the model-stripped upload body kept
by the router filter; apply returned header/body mutations (CONTINUE_AND_REPLACE
when a body is returned). Also call ResponseHeaders, ResponseError for non-2xx
upstream responses, and ResponseBody for content downloads. OpenAI translators
remain no-ops, so OpenAI behaviour is unchanged. This lets non-OpenAI providers
(e.g. GCP) translate Files requests and responses.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Rename parent to routeProcessor and filesRequest to filesRequestMetadata,
and shorten the comments added with the files translator wiring.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
@netlify

netlify Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for theagentrouter ready!

Name Link
🔨 Latest commit 194dfbd
🔍 Latest deploy log https://app.netlify.com/projects/theagentrouter/deploys/6aeef6b2665214227bad5bf9
😎 Deploy Preview https://deploy-preview-2883--theagentrouter.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.13408% with 21 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/extproc/batches_processor.go 93.09% 19 Missing ⚠️
internal/extproc/idrouting_helpers.go 96.92% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

…ration

Move the comment describing the Files API prefix registration to sit directly
above RegisterPrefix, and drop a stray comma in the default seed warning.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Name the codec implementation file after the deterministic AES-GCM scheme it implements; idcodec.go keeps the package API.

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…ngle-backend

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
@sivanantha321
sivanantha321 force-pushed the openai-batch-api-single-backend branch from 9df1d7f to ba29b97 Compare October 22, 2026 11:07
@sivanantha321 sivanantha321 changed the title OpenAI batch api single backend feat: support for OpenAI Batch API with single backend Oct 22, 2026
sivanantha321 and others added 10 commits October 23, 2026 13:10
…or reference

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…ngle-backend

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…ssor

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
… in data-plane tests

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…date endpoint per provider

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
@sivanantha321
sivanantha321 force-pushed the openai-batch-api-single-backend branch from ba29b97 to b8a9b77 Compare October 23, 2026 13:54
sivanantha321 and others added 2 commits November 5, 2026 11:58
…ngle-backend

Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
…Batch API seed configuration

The Files/Batch API id encryption seed is now delivered through the filter
config rather than extproc flags. Test that the Batches processor prefers the
filter config codec, and update the Batch API guide to point at the Helm
values / AIGW_FILE_ID_ENCRYPTION_SEED instead of --fileIDEncryptionSeed.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Sivanantham Chinnaiyan <sivanantham.chinnaiyan@ideas2it.com>
@sivanantha321
sivanantha321 marked this pull request as ready for review November 5, 2026 12:29
@sivanantha321
sivanantha321 requested a review from a team as a code owner November 5, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant