feat(files): serve Anthropic's GA Files API shape and refuse the files beta header - #1509
Merged
Merged
Conversation
peteski22
requested review from
a team and
tbille
and removed request for
a team
September 22, 2026 14:26
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Repository: mozilla-ai/otari/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
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. Comment |
tbille
approved these changes
Sep 22, 2026
The Anthropic flavor of /v1/files serves the files-api-2025-04-14 beta
shape, which Anthropic's GA Files API has replaced. These tests state the
GA contract: every file object carries expires_at, a listing is
{data, next_page} paged by page or read by ids[], after_id and before_id
are refused, and a request carrying the beta header gets a 400 on every
route.
One test drives Anthropic's own SDK through the app: client.files.list()
must page past the first page and retrieve_metadata() must return
expires_at. The ascending-order check moves to the OpenAI flavor, which it
only reached through Anthropic's headers before.
The new tests fail until the following commits land.
Anthropic's GA Files API puts expires_at on every file object, null when the file does not expire, and its SDK reads it from there. The beta shape the gateway served left it out. Both timestamps now go through one RFC 3339 helper, which keeps reading a naive stored value as UTC.
Anthropic's GA Files API lists files as {data, next_page}: the caller
passes next_page back as page, or names up to 100 files with ids[] to read
them in one page. The gateway served the beta's {data, has_more, first_id,
last_id} paged by after_id, so client.files.list() in Anthropic's SDK
stopped after the first page.
The Anthropic flavor now answers in the GA shape. Its page token is the
last file's ID behind a page_ prefix, encoded so that callers treat it as
opaque; a token the gateway did not issue, including one naming another
user's file, is a 400. ids[] cannot be combined with page or limit, and
after_id and before_id get a 400 as they do on Anthropic without the beta
header. The OpenAI flavor keeps its shape and its after cursor.
The public artifacts are regenerated for the changed list parameters.
Anthropic still honors the files-api-2025-04-14 beta and answers it in the beta shapes. The gateway serves one Anthropic contract, the GA one, so a files request whose anthropic-beta header names that beta now gets a 400 that says to send the request without it. Anthropic's Python SDK before 1.2.0, including the locked 0.125.0, sends the header from client.beta.files; client.files does not. The check is a dependency on the router, so a route added to it inherits the refusal. The Anthropic flavor is selected by anthropic-version alone, which Anthropic's SDK sends on every call, rather than also by an anthropic-beta value that starts with files-api.
The Anthropic SDK demo read the produced chart through client.beta.files, which sends the files-api-2025-04-14 beta header that the gateway now refuses. client.files speaks the GA Files API the gateway serves.
The Files API guide described the beta shape and showed client.beta.files. It now names the GA fields, including expires_at, the 400 for the files-api-2025-04-14 header and which SDK releases send it, and how each flavor pages its listing.
peteski22
force-pushed
the
feat/anthropic-files-ga-shape
branch
from
September 22, 2026 14:34
45ce1fc to
f40ef1c
Compare
peteski22
had a problem deploying
to
integration-tests
September 22, 2026 14:34 — with
GitHub Actions
Failure
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
#1366 made Otari's files API answer Anthropic's SDK in Anthropic's own shape. That shape was Anthropic's
files-api-2025-04-14beta, and Anthropic's Files API is now generally available with a different one. This PR moves the Anthropic flavor to the GA shape before it ships in a release:{data, next_page}. Passnext_pageback aspagefor the next page, or name up to 100 files withids[]to read them in one page. With Anthropic's SDK,client.files.list()now pages past the first page.expires_at, which isnullwhen the file does not expire.anthropic-betaheader includesfiles-api-2025-04-14gets a 400 that says to send it without that beta. This is stricter than Anthropic, which still honors the beta, so that Otari keeps one Anthropic contract. The cost:client.beta.fileson Anthropic's Python SDK before 1.2.0, which includes the version in our lock file, gets the 400.client.filesworks.The OpenAI flavor does not change. The code-execution demo and the files guide now use
client.files.For review: the listing query stays in the route, which is on the architecture baseline, because #1483 moves the whole files domain into repositories and a service. A NUL character in the OpenAI
aftercursor or in a file ID path still gives a 500; that was there before this PR, and this PR only guards the two new inputs.How to test it locally
uv run pytest tests/integration/test_files_endpoint.py(needs Docker for PostgreSQL).test_anthropic_sdk_files_client_pages_and_reads_expires_atdrives Anthropic's own SDK through the app: it lists past the first page, readsexpires_at, and gets the 400 fromclient.beta.files. The other new tests cover the page token,ids[], the refusedafter_idandbefore_id, and the 400 on all five routes.curl -H "x-api-key: <key>" -H "anthropic-version: 2023-06-01" "http://localhost:8000/api/v1/files?limit=1"answers{"data": [...], "next_page": ...}.-H "anthropic-beta: files-api-2025-04-14"answers 400.anthropic-version, the answer is the OpenAI list, as before.make lint,make typecheck,make openapi-checkandmake postman-checkpass, and the dashboard client matches the spec. I did not rundemo/code-exec/plot_with_anthropic_sdk.py, which needs provider credentials; the two calls it changed to are the ones the SDK test runs.PR Type
Relevant issues
Fixes #1473. Part of #1470.
Checklist
tests/unit,tests/integration).make lint,make typecheck,make test).uv run python scripts/generate_openapi.py).ARCHITECTURE.mdorscripts/check_architecture.py, the description names the rule and says why.make testpasses locally apart from failures that come from the local environment and not from this change: tests that read the gitignored.env, andtest_mcp_dependency_ceiling, whose throwaway virtual environment cannot load its Python library on this machine.AI Usage
AI Model/Tool used:
Claude Code (Claude Opus 5)
Any additional AI details you'd like to share:
The design comes from the issue. Claude Code wrote the tests first, then the code, the docs and the regenerated artifacts, and ran security, error-handling, state-machine and style checks over the diff, which found three input-handling bugs that are fixed here. Peter reviewed each commit before this PR was opened.
NOTE:
When responding to reviewer questions, please respond yourself rather than copy/pasting reviewer comments into an AI and pasting back its answer. We want to discuss with you, not your AI :)
Summary
Otari now serves Anthropic’s GA Files API shape. Anthropic listings support
pagepagination and up to 100ids[]values, and file metadata always includesexpires_at.Requests that use the legacy
files-api-2025-04-14beta header now return a clear 400 error. The OpenAI Files API remains unchanged.The demo, documentation, client schema, Postman collection, OpenAPI artifacts, and integration coverage were updated. The demo now uses
client.files.Technical notes
{data, next_page}.after_id,before_id, invalid page tokens, and incompatiblepage/ids[]orlimit/ids[]combinations.