Skip to content

fix(batch): make model field optional on POST /v1/batches - #3973

Merged
akshaydeo merged 3 commits into
maximhq:devfrom
alexef:fix/batch-create-model-optional-only
Jun 3, 2026
Merged

fix(batch): make model field optional on POST /v1/batches#3973
akshaydeo merged 3 commits into
maximhq:devfrom
alexef:fix/batch-create-model-optional-only

Conversation

@alexef

@alexef alexef commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #3972

Problem

The OpenAI batch spec does not include a model field on POST /v1/batches — the model lives inside each JSONL request body. The standard OpenAI SDK does not send model on batch create.

batchCreate() in handlers/inference.go called resolveModelAndProvider() unconditionally, which returns "model is required" whenever the field is absent:

// before
provider, modelName, err := resolveModelAndProvider(ctx, h.config, req.Model)
if err != nil {
    SendError(ctx, fasthttp.StatusBadRequest, err.Error())  // always fires without model
    return
}

This was a partial miss from PR #1471 — that PR correctly updated the governance plugin to exempt batch requests from model validation, but the HTTP transport handler was not updated. The "model is required" error therefore still fires at the handler layer before governance is ever consulted.

Confirmed against a live deployment (maximhq/bifrost:v1.5.4):

POST /v1/batches   (no model field, x-model-provider: openai header set)
→ 400 {"is_bifrost_error":false,"status_code":400,"error":{"message":"model is required"}}

POST /v1/batches   (with model: "openai/gpt-4o-mini")
→ 200 OK

Fix

When model is absent, resolve the provider from the ?provider= query param or x-model-provider header — the same fallback pattern used by fileUpload. A provider is still required; only the model is now optional.

Testing

  • Standard OpenAI SDK batch create (no model field) now succeeds when x-model-provider header is set
  • model: "provider/model" still works as before

🤖 Generated with Claude Code

The OpenAI batch spec does not include a model field on POST /v1/batches —
the model lives inside each JSONL request body. The previous implementation
called resolveModelAndProvider unconditionally, which rejected standard
OpenAI SDK calls with "model is required".

When model is absent, fall back to resolving the provider from the
x-model-provider header or ?provider= query param, consistent with the
existing fileUpload handler behaviour.

Closes maximhq#1471 (governance layer was fixed; this fixes the HTTP handler layer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b0f7e3b7-12a7-4d79-83b2-51ef53bc40c6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant

CLAassistant commented Jun 2, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ alexef
❌ akshaydeo
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

Safe to merge — the change is narrowly scoped to the batch create handler, preserves all existing model-present behavior, and the new no-model path mirrors an established pattern already in use for file uploads.

The fix is targeted and correct: the new helper correctly delegates to resolveModelAndProvider when a model string is present and handles the no-model case by reading the same provider sources (?provider= / x-model-provider) already used by fileUpload. All four meaningful resolution paths are covered by the new tests, including the error case. No existing behavior is altered.

No files require special attention.

Important Files Changed

Filename Overview
transports/bifrost-http/handlers/inference.go Adds resolveBatchProvider helper making model optional on POST /v1/batches; provider is still required via ?provider= or x-model-provider header. Logic correctly mirrors the fileUpload pattern.
transports/bifrost-http/handlers/inference_batch_test.go New table-driven tests cover all three resolution paths: model present, model absent + header, model absent + query param, and error case when neither is supplied.

Reviews (3): Last reviewed commit: "Merge branch 'main' into fix/batch-creat..." | Re-trigger Greptile

Comment thread transports/bifrost-http/handlers/inference.go Outdated
Extract the provider-resolution logic from batchCreate into resolveBatchProvider
so it can be tested in isolation, then add a table-driven test covering all three
resolution paths: model field, x-model-provider header, ?provider= query param,
and the error case where neither provider nor model is supplied.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
akshaydeo
akshaydeo previously approved these changes Jun 3, 2026
@akshaydeo
akshaydeo changed the base branch from main to dev June 3, 2026 10:17
@akshaydeo
akshaydeo dismissed their stale review June 3, 2026 10:17

The base branch was changed.

@akshaydeo
akshaydeo merged commit 7474c06 into maximhq:dev Jun 3, 2026
4 of 5 checks passed
akshaydeo added a commit that referenced this pull request Jun 4, 2026
* fix(batch): make model field optional on POST /v1/batches

The OpenAI batch spec does not include a model field on POST /v1/batches —
the model lives inside each JSONL request body. The previous implementation
called resolveModelAndProvider unconditionally, which rejected standard
OpenAI SDK calls with "model is required".

When model is absent, fall back to resolving the provider from the
x-model-provider header or ?provider= query param, consistent with the
existing fileUpload handler behaviour.

Closes #1471 (governance layer was fixed; this fixes the HTTP handler layer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(batch): add unit tests for resolveBatchProvider

Extract the provider-resolution logic from batchCreate into resolveBatchProvider
so it can be tested in isolation, then add a table-driven test covering all three
resolution paths: model field, x-model-provider header, ?provider= query param,
and the error case where neither provider nor model is supplied.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Akshay Deo <akshay@akshaydeo.com>
@alexef
alexef deleted the fix/batch-create-model-optional-only branch June 6, 2026 20:37
@akshaydeo akshaydeo mentioned this pull request Jun 7, 2026
akshaydeo added a commit that referenced this pull request Jun 7, 2026
* fix(batch): make model field optional on POST /v1/batches

The OpenAI batch spec does not include a model field on POST /v1/batches —
the model lives inside each JSONL request body. The previous implementation
called resolveModelAndProvider unconditionally, which rejected standard
OpenAI SDK calls with "model is required".

When model is absent, fall back to resolving the provider from the
x-model-provider header or ?provider= query param, consistent with the
existing fileUpload handler behaviour.

Closes #1471 (governance layer was fixed; this fixes the HTTP handler layer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(batch): add unit tests for resolveBatchProvider

Extract the provider-resolution logic from batchCreate into resolveBatchProvider
so it can be tested in isolation, then add a table-driven test covering all three
resolution paths: model field, x-model-provider header, ?provider= query param,
and the error case where neither provider nor model is supplied.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Akshay Deo <akshay@akshaydeo.com>
akshaydeo added a commit that referenced this pull request Jun 7, 2026
## ✨ Features

- **OpenAI Compaction** — Added OpenAI conversation compaction support
across core, framework, logging, and the API surface (#4053)
- **Multi-Customer & Org Hierarchy** — Logs and usage tracking now
support multiple customers, teams, and business units, including
business unit CRUD, team assignment, and governance endpoints in the
OpenAPI spec (#4066, #4041, #4082)
- **Provider-Level Governance** — Budgets & limits are now scope-aware
and can be applied at the virtual-key top level and per provider, wired
from the model configs table, with UI filters for scope and providers
(#3938, #3937, #3939, #3981, #3962)
- **Customer Budgets** — Customers support multiple budgets and
`calendar_aligned` budget windows (#3998, #3997)
- **Virtual Key Attribution & Controls** — Added a `created_by` user
attribution column and a `blacklisted_models` column for virtual key
provider configs (#3672, #3653)
- **Request Header Capture** — OTel and Maxim observability plugins
capture `request_headers` by pattern, with wildcard support (e.g.
`x-custom-*`); logging gained the same wildcard header capture (#4012,
#3958)
- **OTel Content Controls & Collectors** — New `disable_content_logging`
option drops message/tool content from exported spans, plus support for
multiple OTel collectors (#4064, #3894)
- **xAI x_search** — Added xAI `x_search` tool support (#3976)
- **URL Validation** — Added fetch URL validation with private-network
configuration and link-local blocking (#3947, #3991)
- **File Scheme Pricing URLs** — Pricing source URLs now accept the
`file://` scheme for air-gapped and self-hosted deployments (#4045)
- **Paginated Virtual Keys** — Virtual key fetching is paginated to
handle deployments with very large numbers of keys (#3957)
- **Client IP Resolution** — Resolve client IP from
`X-Forwarded-For`/`X-Real-IP` headers
- **SCIM Provisioning** — Added `attributeType`/`attributeValue` SCIM
provisioning fields
- **Helm/Config Schema** — Added `roles` RBAC governance config and
`per_user_oauth` MCP auth to the Helm chart and config schema (#4004,
#4009)
- **Log Navigation UI** — Added a "View logs" menu item to customer,
team, and virtual key tables, clickable links in log detail views, a
customer detail sheet, and a reusable `BudgetDisplay` component (#4073,
#4054, #4026, #4055)
- **Faster First Paint** — Added an inline loading shell to `#root`
before React mounts (#4063)
- **Materialized View Alias** — Added an `alias` column to the
materialized view with filter support (#4078)

## 🐞 Fixed

- **Fetch URL IP Checks** — Hardened fetch URL IP checks against SSRF
(#4092)
- **Mantle Model Matching** — Broadened Mantle model matching to all
`gpt` variants (#4091)
- **Empty Thinking Blocks** — Strip thinking blocks when the signature
is empty (#4079)
- **OpenAI Stream Usage** — Removed usage from the `responses.created`
event in the OpenAI stream (#4080)
- **Prompt Cache Key** — Set the prompt cache key from the Anthropic
integration (#4086)
- **Upstream Failure Status** — Map upstream connection failures to 502
instead of 400 (#3929) (thanks
[@chris-colinsky](https://github.com/chris-colinsky)!)
- **Gemini Schema Constraints** — Accept numeric schema integer
constraints for Gemini (#3994) (thanks
[@yanhao98](https://github.com/yanhao98)!)
- **Files Provider Param** — Accept the `?provider=` query param on `GET
/v1/files` (#3971) (thanks [@alexef](https://github.com/alexef)!)
- **Optional Batch Model** — Made the `model` field optional on `POST
/v1/batches` (#3973) (thanks [@alexef](https://github.com/alexef)!)
- **Helm Azure Config** — Added missing `azure_key_config` fields to the
Helm schema (#3996) (thanks
[@axelray-dev](https://github.com/axelray-dev)!)
- **Text Completion Chunk Model** — Added the missing `Model` field to
`TextCompletionChunkResponse` (#3970) (thanks
[@kuishou68](https://github.com/kuishou68)!)
- **MCP Inline stdio Env** — MCP stdio server configs accept inline
environment variable assignments (#3861) (thanks
[@Shushmitaaaa](https://github.com/Shushmitaaaa)!)
- **Orphaned Tool Results** — Orphaned tool results in the OpenAI to
Anthropic conversion flow are no longer rejected by the Anthropic API
(#3919)
- **Node Usage Reconciliation** — Added a monotonic `inc_number` log
cursor so node usage reconciliation does not skip late async log writes
(#3664)
- **Bedrock Output Assessments** — Corrected the type of
`outputAssessments` in Bedrock responses (#4028)
- **Model Pool Pricing Reloads** — Preserve non-pricing model pool
entries across pricing reloads (#3999)
- **Ghost Node Reconciliation** — Replicate the VK hierarchy flow for
ghost node reconciliation (#4088)
- **VK Double Usage Counting** — Fixed double usage counting when
creating a virtual key (#4070)
- **Model Config Lifecycle** — Cascade deletes for model configs and
removal of stale in-memory model configs (#4051, #4043)
- **FTS Index Cap** — Reduced the FTS index `left()` cap from 800k to
250k chars to stay within the tsvector limit (#4057)
- **Sync Worker Drift** — Reduced the sync worker ticker period to 5m to
prevent threshold drift (#4023)
- **Passthrough** — Fixed passthrough budgets, gated passthrough models
per VK, model extraction for Azure passthrough, and restricted
fallbacks/provider selection to the VK boundary (#3941, #3988, #3983,
#3924)
- **Provider Response Headers** — Strip provider response headers and
add a content-type filter (#3955, #4024)
- **Stream Handling** — Drain non-SSE stream readers and retry stale
connections (#3956, #3967)
- **Azure Claude** — Strip Azure diagnostic property for Claude models
(#3925)
- **Compat max_tokens** — Preserve chat `max_tokens` during param
filtering (#3992)
- **Raw Request Flag** — Removed the raw request flag from providers
that don't support it (#4058)
- **UI Fixes** — Standardized page container layout, virtual key model
configs UI, and dashboard chart tooltips (#4046, #4052, #4044)

## 🔧 Maintenance

- **Dependency Upgrades** — Bumped transitive `golang.org/x`
dependencies (crypto, net, sys, text) for Docker Scout CVE remediation
and `recharts` to 3.8.1; cascaded version bumps across all modules
(#3900, #4003)
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.

[Bug]: POST /v1/batches requires non-standard model field rejected by standard OpenAI SDK

3 participants