Skip to content

feat!: add Anthropic Message Batches API#5577

Merged
leseb merged 8 commits into
ogx-ai:mainfrom
cdoern:feat/messages-batches-api
May 13, 2026
Merged

feat!: add Anthropic Message Batches API#5577
leseb merged 8 commits into
ogx-ai:mainfrom
cdoern:feat/messages-batches-api

Conversation

@cdoern

@cdoern cdoern commented Apr 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds all 5 Anthropic Message Batches API endpoints under /v1/messages:

  • POST /v1/messages/batches — create a batch of message requests
  • GET /v1/messages/batches — list batches with pagination
  • GET /v1/messages/batches/{id} — retrieve batch status
  • POST /v1/messages/batches/{id}/cancel — cancel an in-progress batch
  • GET /v1/messages/batches/{id}/results — stream results as JSONL

The implementation follows the same architecture as the existing OpenAI Batches provider: KVStore persistence, background asyncio tasks with semaphore-based concurrency control, and reuse of the existing create_message() flow for processing individual batch requests.

Breaking change

MessagesConfig now requires a kvstore backend (KVStoreReference). Existing distributions are updated via codegen; out-of-tree configs will need to add this field.

Test plan

  • Integration tests added at tests/integration/messages/test_message_batches.py covering create / retrieve / list / cancel / results / duplicate-custom_id / 404, running under the messages-openai suite (already wired into tests/integration/ci_matrix.json)
  • Recordings committed against openai/gpt-4o so replay-mode CI works without an API key
  • Pre-commit checks pass (ruff, mypy, API spec, Anthropic coverage)
  • Unit tests pass
  • OpenAPI spec auto-regenerated with new endpoints
  • Distribution configs (ci-tests, starter) updated via codegen

Locally verified:

./scripts/integration-tests.sh \
  --stack-config server:ci-tests --setup gpt \
  --file tests/integration/messages/test_message_batches.py
# 6 passed in 1.76s

🤖 Generated with Claude Code

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Apr 16, 2026
@github-actions

github-actions Bot commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

✱ Stainless preview builds

This PR will update the llama-stack-client SDKs with the following commit message.

feat(messages): add Anthropic Message Batches API
⚠️ llama-stack-client-node studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️build ⏭️lint ⏭️test ✅

⚠️ llama-stack-client-openapi studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️

⚠️ llama-stack-client-go studio · conflict

Your SDK build had at least one warning diagnostic.

⚠️ llama-stack-client-python studio · code

Your SDK build had at least one "warning" diagnostic.
generate ⚠️build ⏭️lint ⏭️test ✅


This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-05-13 15:42:32 UTC

@cdoern cdoern changed the title feat(messages): add Anthropic Message Batches API feat(messages)!: add Anthropic Message Batches API Apr 16, 2026
@cdoern
cdoern force-pushed the feat/messages-batches-api branch 2 times, most recently from 7f7488d to 0ed9397 Compare April 16, 2026 19:35
@ogx-ai ogx-ai deleted a comment from nidhishgajjar Apr 22, 2026
@ogx-ai ogx-ai deleted a comment from nidhishgajjar Apr 22, 2026
skamenan7 pushed a commit to skamenan7/llama-stack that referenced this pull request Apr 22, 2026
…ranslation (ogx-ai#5587)

## Summary

- Adds a new `messages-openai` integration test suite that reuses
`tests/integration/messages` with the `gpt` setup, so requests flow
through `_anthropic_to_openai` / `_openai_to_anthropic` in
`providers/inline/messages/impl.py` instead of the native passthrough
branch that the existing `messages` (ollama-reasoning) suite hits.
- Registers the suite in `tests/integration/ci_matrix.json` so
replay-mode CI runs it, and adds a `gpt` / `messages-openai` row to
`.github/workflows/record-integration-tests.yml` so future rerecord runs
refresh it.
- Includes 12 recordings captured against `openai/gpt-4o` so replay mode
works out-of-the-box (no API key required in CI).

This is a precursor to ogx-ai#5577 — the batches tests there need a suite that
targets OpenAI, because batches go through `create_message()` which only
exercises translation when the provider is not in
`_NATIVE_MESSAGES_MODULES`.

## Test plan

Recorded locally and verified against `openai/gpt-4o`:

```bash
./scripts/integration-tests.sh \
  --stack-config server:ci-tests \
  --suite messages-openai \
  --inference-mode record-if-missing
```

Result: 13/13 tests pass. Recording URLs point at
`https://api.openai.com/v1/chat/completions`, confirming the translation
path is being exercised (not the Anthropic-native `/v1/messages`
endpoint the ollama suite hits).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Charlie Doern <[email protected]>
@cdoern
cdoern force-pushed the feat/messages-batches-api branch 4 times, most recently from 70091ac to 25dae2f Compare April 22, 2026 19:28
@cdoern
cdoern marked this pull request as ready for review April 22, 2026 19:47
@cdoern

cdoern commented Apr 22, 2026

Copy link
Copy Markdown
Collaborator Author

not sure why backwards compatibility test is failing, I added ! in.

@cdoern
cdoern force-pushed the feat/messages-batches-api branch from 25dae2f to f9f0570 Compare May 5, 2026 13:39
Add the 5 Anthropic Message Batches endpoints under /v1/messages:

- POST /v1/messages/batches (create batch)
- GET /v1/messages/batches (list batches)
- GET /v1/messages/batches/{id} (retrieve batch)
- POST /v1/messages/batches/{id}/cancel (cancel batch)
- GET /v1/messages/batches/{id}/results (stream JSONL results)

The implementation follows the same pattern as the existing OpenAI Batches
provider: KVStore for persistence, background asyncio tasks with
semaphore-based concurrency control, and reuse of the existing
create_message() flow for processing individual batch requests. The
MessagesConfig now requires a kvstore backend, which is a breaking change
to the provider configuration.

Integration tests run under the messages-openai suite and cover create /
retrieve / list / cancel / results / duplicate-custom_id / 404 paths against
openai/gpt-4o, with recordings committed for replay-mode CI.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Charlie Doern <[email protected]>
@cdoern
cdoern force-pushed the feat/messages-batches-api branch from f9f0570 to d63702b Compare May 5, 2026 14:11
@cdoern cdoern changed the title feat(messages)!: add Anthropic Message Batches API feat!: add Anthropic Message Batches API May 5, 2026

@leseb leseb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, AI caught this one too:

 Cancel during processing can misreport completed requests as canceled

  Results are only persisted once after asyncio.gather completes, but cancellation finalization depends on previously persisted results (existing_data). If cancellation happens mid-run, completed requests are not yet saved, so _finalize_batch_canceled marks all not-yet-recorded requests as canceled, losing true
  succeeded/errored outcomes and producing incorrect counts.

Comment on lines +200 to +202
limit: int = 20,
before_id: str | None = None,
after_id: str | None = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a ListMessageBatchesRequest


async def retrieve_message_batch_results(
self,
batch_id: str,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto for Request class


async def _process_message_batch(
self,
batch_id: str,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why isn't batch_id part of the request class?


async def _process_message_batch_impl(
self,
batch_id: str,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto


async def _finalize_batch_canceled(
self,
batch_id: str,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Comment thread src/ogx_api/messages/models.py Outdated
requests: list[MessageBatchRequestParams] = Field(
...,
min_length=1,
max_length=100000,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't 100000 a little too much for asyncio, let's do 1000 instead?

if batch.processing_status == "ended":
raise ValueError(f"Cannot cancel batch '{batch_id}' that has already ended")

async with self._update_lock:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's acquire the lock earlier so we avoid the read-check-write race? we need to get the batch in the lock too, then cancel the task after releasing the lock.

cdoern added 2 commits May 12, 2026 14:13
- Introduce request classes for list/retrieve/cancel/results endpoints and
  thread them through the Messages protocol, FastAPI routes, and inline impl.
- Lower CreateMessageBatchRequest.requests max_length from 100000 to 1000
  to keep batch size reasonable for asyncio scheduling.
- Bundle batch_id with its original request via an internal _BatchContext
  so private helpers take a single argument.
- Fix cancel_message_batch race by reading and updating the batch under
  the update lock, and cancelling the processing task only after the lock
  is released so the task can acquire it to finalize.
- Track partial batch results in memory as each request completes so a
  mid-run cancellation finalizes with the true succeeded/errored counts
  rather than marking already-completed requests as canceled.

Signed-off-by: Charlie Doern <[email protected]>
cdoern added 2 commits May 12, 2026 14:38
The messages suite defaults to the ollama setup which uses
ollama/llama3.2:3b-instruct-fp16, but the recorded HTTP responses for
the batch tests only existed for ollama/gpt-oss:20b. The ollama suite
batch tests therefore had no matching recording and fell through to the
live container which returned 400. Add llama3.2-keyed recordings cloned
from the existing gpt-oss responses so replay covers both parameter
variants.

Signed-off-by: Charlie Doern <[email protected]>
@cdoern
cdoern requested review from leseb and removed request for ashwinb and ehhuang May 12, 2026 18:43
The messages suite was switched from ollama-reasoning (gpt-oss:20b) to
ollama (llama3.2:3b-instruct-fp16) in ogx-ai#5746, so the gpt-oss-keyed batch
recordings are never consulted in replay. Flagged by the
unused-recordings check.

Signed-off-by: Charlie Doern <[email protected]>

@leseb leseb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one miss?

Comment thread src/ogx_api/messages/fastapi_routes.py Outdated
Comment on lines +265 to +267
limit: int = 20,
before_id: str | None = None,
after_id: str | None = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be ListMessageBatchesRequest?

…ssageBatchesRequest

Addresses review feedback on PR ogx-ai#5577: the GET /messages/batches route now
takes a ListMessageBatchesRequest directly via Annotated[..., Query()] instead
of unpacking the individual query parameters and reconstructing the request
class inside the handler.

Signed-off-by: Charlie Doern <[email protected]>
@cdoern
cdoern requested a review from leseb May 13, 2026 15:11
@leseb
leseb enabled auto-merge May 13, 2026 15:18
@leseb
leseb added this pull request to the merge queue May 13, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 13, 2026
@leseb
leseb added this pull request to the merge queue May 13, 2026
Merged via the queue into ogx-ai:main with commit 479af78 May 13, 2026
66 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants