Skip to content

fix: return file bytes from GET /v1/files/{file_id}/content on the anthropic surface - #5706

Open
brianhadley2026 wants to merge 3 commits into
maximhq:devfrom
Danbury-Rock:fix/anthropic-files-content-route
Open

fix: return file bytes from GET /v1/files/{file_id}/content on the anthropic surface#5706
brianhadley2026 wants to merge 3 commits into
maximhq:devfrom
Danbury-Rock:fix/anthropic-files-content-route

Conversation

@brianhadley2026

@brianhadley2026 brianhadley2026 commented Jul 30, 2026

Copy link
Copy Markdown

Summary

On the anthropic surface, GET /v1/files/{file_id}/content returns the file's metadata JSON with HTTP 200 instead of the file bytes. The route config mounts the metadata-retrieve handler on the /content path: it instantiates AnthropicFileRetrieveRequest and dispatches FileRequest{Type: schemas.FileRetrieveRequest, ...} (the comment above it says "Retrieve file endpoint - GET /v1/files/{file_id}"). Meanwhile the intended metadata endpoint, GET /v1/files/{file_id}, is not routed at all.

Because the failure is a well-formed 200, clients can't detect it — and files generated server-side by Anthropic's code-execution/skills tools (pptx/xlsx/docx) cannot be downloaded through Bifrost at all. Observed on v1.5.11, v1.6.4, and v1.6.6.

Everything below the route config already works — AnthropicProvider.FileContent fetches {baseURL}/v1/files/{id}/content correctly, and the generic router's schemas.FileContentRequest case writes raw bytes with the upstream content-type. The content path was simply never dispatched.

Changes

  • transports/bifrost-http/integrations/anthropic.go — re-mount the existing retrieve logic on its intended path, GET /v1/files/{file_id} (previously unrouted), and add a content route for GET /v1/files/{file_id}/content that instantiates AnthropicFileContentRequest (already handled by extractAnthropicFileIDFromPath) and dispatches FileRequest{Type: schemas.FileContentRequest, ContentRequest: ...}, mirroring the Bedrock S3 GetObject route. No response converter is set, so the generic router's default binary path writes the bytes with the upstream content-type. The Gemini files-files/ id translation is preserved on both routes.
  • transports/bifrost-http/integrations/anthropic_files_test.go — regression tests pinning both routes' dispatch: the content route must produce a ContentRequest (never a RetrieveRequest), the retrieve route lives on the metadata path, and the Gemini id translation holds.
  • transports/changelog.md — changelog entry.

Type of change

  • Bug fix

Affected areas

  • Transports (HTTP)

How to test

cd transports
go test ./bifrost-http/integrations/ -run AnthropicFiles -v

Live verification (anthropic provider key + virtual key):

# a code-execution-generated file id (or any downloadable file)
curl -s http://localhost:8080/anthropic/v1/files/file_.../content \
  -H "x-api-key: $VK" -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: files-api-2025-04-14" -o out.xlsx

Before: HTTP 200, application/json, metadata body. After: HTTP 200 with the file bytes — verified SHA256-identical to the direct-api.anthropic.com download of the same file id, with the correct OOXML content-type. A non-downloadable (user-uploaded) file now relays upstream's 400 invalid_request_error "File '...' is not downloadable" verbatim instead of a silent 200. GET /v1/files/{id} returns the metadata JSON; list/upload/delete unchanged.

Screenshots/Recordings

N/A (no UI changes).

Breaking changes

  • Yes
  • No

Callers that inadvertently relied on /content returning metadata JSON should use GET /v1/files/{file_id}, which this PR routes.

Related issues

Relates to #5707 (string-form container reuse silently dropped on the same surface — separate defect, not addressed here).

Security considerations

None — the route relays bytes the caller's virtual key is already authorized to fetch; auth and governance attribution paths are unchanged.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed (no doc pages reference the files routes)
  • I verified builds succeed (Go and UI, via transports/Dockerfile)
  • I verified the CI pipeline passes locally if applicable (go test ./bifrost-http/integrations/)

🤖 Generated with Claude Code

jeremym-tanium and others added 2 commits July 31, 2026 00:08
…iew maintenance (maximhq#5693)

* feat: support matview_refresh_interval "off" to disable logstore matview maintenance

The materialized views back only the dashboard UI. Deployments that run
Bifrost headless behind their own observability stack pay the REFRESH
MATERIALIZED VIEW CONCURRENTLY cost for views nothing reads, and the 5s
floor means the interval alone cannot turn maintenance off.

With "off" (or a non-positive duration) the logs store skips view
creation, the initial refresh, and the periodic refresher entirely.
matViewsReady stays false, so dashboard queries fall back to the raw
tables, and the runtime self-heal path cannot re-arm maintenance since
it only triggers from matview-path queries.

* fix: guard matview self-heal when maintenance is disabled

Review follow-up: carry the resolved disabled state onto the store so
triggerMatViewSelfHeal cannot recreate views the configuration says must
not exist, and make the schema/docs explicit that a zero duration also
disables (positive sub-5s values still clamp up).
…thropic surface

The anthropic files route config mounted the metadata-retrieve handler on the
/content path: it instantiated AnthropicFileRetrieveRequest and dispatched
schemas.FileRetrieveRequest, so a download request returned the file metadata
JSON with HTTP 200 instead of the file bytes. AnthropicProvider.FileContent and
the generic router's FileContentRequest byte-writing path already work; they
were simply never reached.

- Re-mount the existing retrieve logic on its intended path,
  GET /v1/files/{file_id} (previously unrouted).
- Add a content route for GET /v1/files/{file_id}/content that dispatches
  schemas.FileContentRequest; the router's default binary path writes the
  bytes with the upstream content-type.

Repro (before): upload any file via POST /anthropic/v1/files, then
GET /anthropic/v1/files/{id}/content -> 200 application/json metadata,
while the same request direct to api.anthropic.com returns the file bytes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@CLAassistant

CLAassistant commented Jul 30, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added Anthropic-compatible file routing for both file details (GET /v1/files/{file_id}) and file content (GET /v1/files/{file_id}/content).
    • Added configuration to disable materialized-view maintenance via matview_refresh_interval set to "off" or "0s".
  • Bug Fixes

    • Fixed Anthropic file content retrieval to return file bytes correctly.
    • Prevented background materialized-view refresh/self-heal from starting when maintenance is disabled.
  • Documentation

    • Updated deployment/config guidance to explain clamping, disabling behavior, defaults, and when it’s safe to turn maintenance off.

Walkthrough

Changes

Materialized-view maintenance

Layer / File(s) Summary
Interval resolution and configuration contract
framework/logstore/postgres.go, transports/config.schema.json, docs/deployment-guides/config-json/storage.mdx
"off" and non-positive durations disable maintenance; positive values below 5 seconds remain clamped.
Maintenance lifecycle gating
framework/logstore/rdb.go, framework/logstore/postgres.go, framework/logstore/matviews.go, framework/logstore/matviewheal.go
Disabled state prevents matview startup, refresher creation, and self-healing.
Disabled maintenance validation
framework/logstore/matviews_lock_test.go
Tests cover disabled interval parsing, no-op refresher shutdown, and skipped self-healing.

Anthropic file route separation

Layer / File(s) Summary
File retrieval and content route wiring
transports/bifrost-http/integrations/anthropic.go, transports/bifrost-http/integrations/anthropic_files_test.go, transports/changelog.md
Separates file retrieval and content endpoints, normalizes Gemini file identifiers, validates route dispatch, and documents the content retrieval fix.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Config as matview_refresh_interval
  participant Store as newPostgresLogStore
  participant Refresher as startMatViewRefresher
  participant SelfHeal as triggerMatViewSelfHeal
  Config->>Store: resolve "off" or non-positive duration
  Store->>Refresher: skip startup when refreshInterval <= 0
  Store->>SelfHeal: set matViewMaintenanceDisabled
  SelfHeal-->>Store: return without scheduling self-heal
Loading

Possibly related PRs

Suggested reviewers: pratham-mishra04, impoiler, roroghost17, tejasghatte, akshaydeo

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the primary fix to return file bytes from the Anthropic content endpoint.
Description check ✅ Passed The description follows the template, clearly explains the bug, changes, testing, affected area, risks, and checklist status.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
transports/bifrost-http/integrations/anthropic.go (1)

1037-1040: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add table-driven regression coverage for the route split.

Extend transports/bifrost-http/integrations/anthropic_test.go to verify metadata retrieval, byte-identical content responses with upstream Content-Type, upstream error conversion, and Gemini files- normalization.

As per coding guidelines, Go behavior changes should have deterministic, table-driven coverage.

Also applies to: 1074-1107

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@transports/bifrost-http/integrations/anthropic.go` around lines 1037 - 1040,
Extend the tests around the Anthropic file routes in anthropic_test.go with
deterministic table-driven cases covering metadata retrieval, byte-identical
content bodies while preserving the upstream Content-Type, upstream error
conversion, and normalization of Gemini file identifiers beginning with
“files-”. Reuse the existing HTTP test helpers and assert each route’s response
status, headers, and body.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/deployment-guides/config-json/storage.mdx`:
- Around line 280-282: Update the matview_refresh_interval description in the
configuration table to state that any non-positive duration disables
materialized view maintenance, including an example such as "-1m", while
retaining the existing "off" and "0s" examples and positive-duration behavior.

---

Nitpick comments:
In `@transports/bifrost-http/integrations/anthropic.go`:
- Around line 1037-1040: Extend the tests around the Anthropic file routes in
anthropic_test.go with deterministic table-driven cases covering metadata
retrieval, byte-identical content bodies while preserving the upstream
Content-Type, upstream error conversion, and normalization of Gemini file
identifiers beginning with “files-”. Reuse the existing HTTP test helpers and
assert each route’s response status, headers, and body.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 401f469f-f648-4691-8eff-efadfdce1941

📥 Commits

Reviewing files that changed from the base of the PR and between 22f98fc and 129875c.

📒 Files selected for processing (8)
  • docs/deployment-guides/config-json/storage.mdx
  • framework/logstore/matviewheal.go
  • framework/logstore/matviews.go
  • framework/logstore/matviews_lock_test.go
  • framework/logstore/postgres.go
  • framework/logstore/rdb.go
  • transports/bifrost-http/integrations/anthropic.go
  • transports/config.schema.json

Comment on lines 280 to +282
| Field | Default | Description |
|-------|---------|-------------|
| `matview_refresh_interval` | `"1m"` | How often to refresh dashboard materialized views. Accepts any Go duration string (`"1m"`, `"5m"`, `"1h"`). Minimum `5s`. |
| `matview_refresh_interval` | `"1m"` | How often to refresh dashboard materialized views. Accepts any Go duration string (`"1m"`, `"5m"`, `"1h"`); positive values below `5s` are clamped up to `5s`. Set `"off"` or a zero duration (`"0s"`) to disable matview maintenance entirely. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Document all non-positive disable values.

The runtime treats any non-positive duration as disabled, but this page only documents "0s". Update the description to say "off" or any non-positive duration, with "-1m" as an example, so config.json guidance matches the implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/deployment-guides/config-json/storage.mdx` around lines 280 - 282,
Update the matview_refresh_interval description in the configuration table to
state that any non-positive duration disables materialized view maintenance,
including an example such as "-1m", while retaining the existing "off" and "0s"
examples and positive-duration behavior.

Source: Path instructions

@brianhadley2026
brianhadley2026 changed the base branch from main to dev July 30, 2026 22:47
…gelog entry

Regression tests for the /content route fix: the content route must dispatch
FileContentRequest with a populated ContentRequest (never RetrieveRequest),
the retrieve handler lives on GET /v1/files/{file_id}, and the Gemini
files- -> files/ id translation holds on the content path.

Affected packages:
- transports/bifrost-http/integrations (test only)
- transports/changelog.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai
coderabbitai Bot requested a review from TejasGhatte July 30, 2026 22:55
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 30, 2026
@akshaydeo
akshaydeo dismissed coderabbitai[bot]’s stale review August 5, 2026 07:18

The merge-base changed after approval.

@akshaydeo
akshaydeo force-pushed the dev branch 2 times, most recently from d025e9c to d704af4 Compare August 10, 2026 22:20
@akshaydeo
akshaydeo force-pushed the dev branch 3 times, most recently from 1eaa684 to 2ed4dd9 Compare August 19, 2026 08:15
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.

3 participants