fix: adds file id for anthropic files integration and adds forwarding of content type - #4956
Conversation
📝 WalkthroughWalkthroughThe PR adds Anthropic file_id handling across response conversion and beta header detection, preserves multipart upload Content-Type values through Anthropic and OpenAI upload paths, and includes formatting-only updates in related type and route code. ChangesFile ID and upload content-type support
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant HTTPHandlers
participant AnthropicIntegration
participant AnthropicProvider
participant AnthropicUtils
Client->>HTTPHandlers: Multipart upload with file part
HTTPHandlers->>HTTPHandlers: Capture file part Content-Type
HTTPHandlers->>AnthropicIntegration: Parsed upload request with ContentType
AnthropicIntegration->>AnthropicProvider: BifrostFileUploadRequest with ContentType
AnthropicProvider->>AnthropicProvider: Build MIMEHeader and create multipart part
Client->>AnthropicUtils: Message content block with file source
AnthropicUtils->>AnthropicUtils: Detect source.type=="file", append files-api header
AnthropicUtils->>AnthropicUtils: ConvertResponsesFileBlockToAnthropic emits type:"file"/file_id
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
3e5358b to
3d5ae28
Compare
There was a problem hiding this comment.
Pull request overview
This PR extends the Anthropic integration to (1) preserve/forward file upload MIME types end-to-end and (2) support referencing already-uploaded files via file_id in document/image content blocks, including automatic injection of the required Anthropic Files API beta header when such blocks are present.
Changes:
- Propagates an optional
content_typefrom the HTTP multipart upload throughBifrostFileUploadRequestand into Anthropic multipart file upload construction. - Adds conversion support for
source.type = "file"/file_idblocks in Anthropic Responses ↔ Bifrost schema mapping. - Adds Files API beta header auto-injection when requests include
"file"-sourced content blocks.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| transports/bifrost-http/integrations/anthropic.go | Reads content_type from multipart form data (or falls back to the file part header) and forwards it into the core upload request. |
| core/schemas/responses.go | Moves file_id onto the embedded file content block type so it lives alongside other file fields. |
| core/providers/anthropic/utils.go | Adds detection of "file" sources in message blocks to auto-inject the Files API beta header. |
| core/providers/anthropic/types.go | Extends Anthropic file upload request shape with optional content_type and includes formatting-only adjustments. |
| core/providers/anthropic/responses.go | Maps Anthropic "file" sources back into ResponsesInputMessageContentBlockFile.FileID. |
| core/providers/anthropic/anthropic.go | Builds multipart parts with an explicit MIME header so the file part can carry the caller-supplied Content-Type. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@core/providers/anthropic/anthropic.go`:
- Around line 2173-2180: The multipart file upload header in the Anthropic file
creation flow now drops Content-Type when request.ContentType is unset, changing
the previous behavior from writer.CreateFormFile. Update the header construction
around multipart.FileContentDisposition/CreatePart so that it still sets a
default Content-Type of application/octet-stream when request.ContentType is nil
or trims to empty, and only overrides it when a non-empty value is provided.
- Around line 2173-2180: The multipart part construction in anthroptic request
handling is using an unsanitized Content-Type value, allowing header injection
through the outgoing request. In the code path that builds the MIME header
before writer.CreatePart, validate request.ContentType by rejecting any value
containing CR/LF and preferably parsing it with mime.ParseMediaType before
calling header.Set("Content-Type", ...). Keep the existing filename handling
as-is, and ensure only a well-formed media type is written into the header.
In `@core/providers/anthropic/utils.go`:
- Around line 1207-1223: The file-source scan in the message loop stops too
early when it hits a text-only message because the
`message.Content.ContentBlocks == nil` branch in the `hasFileSource` logic uses
the wrong flow control. Update the scan in `utils.go` so the outer loop skips
messages with nil `ContentBlocks` instead of terminating, while keeping the
existing `hasFileSource` and `appendUniqueHeader` behavior intact, so later file
blocks can still trigger `AnthropicFilesAPIBetaHeader`.
🪄 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: 170f4580-ad80-4263-b8c1-dd483f541127
📒 Files selected for processing (6)
core/providers/anthropic/anthropic.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/schemas/responses.gotransports/bifrost-http/integrations/anthropic.go
3d5ae28 to
64c64ce
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
transports/bifrost-http/integrations/openai.go (1)
2569-2574: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider extracting the content-type fallback into a shared helper.
This 3-line precedence pattern (form field → multipart part
Content-Typeheader) is duplicated verbatim intransports/bifrost-http/handlers/inference.go'sfileUpload. Extracting a small helper (e.g.,resolveMultipartContentType(form, fileHeader) *string) would keep the two upload paths from silently diverging if the precedence logic changes later.♻️ Proposed shared helper
// resolveMultipartContentType returns the caller-supplied content_type form // field, falling back to the uploaded file part's Content-Type header. func resolveMultipartContentType(form *multipart.Form, fileHeader *multipart.FileHeader) *string { if values := form.Value["content_type"]; len(values) > 0 && values[0] != "" { return &values[0] } if partContentType := strings.TrimSpace(fileHeader.Header.Get("Content-Type")); partContentType != "" { return &partContentType } return nil }- if contentTypeValues := form.Value["content_type"]; len(contentTypeValues) > 0 && contentTypeValues[0] != "" { - uploadReq.ContentType = &contentTypeValues[0] - } else if partContentType := strings.TrimSpace(fileHeader.Header.Get("Content-Type")); partContentType != "" { - uploadReq.ContentType = &partContentType - } + uploadReq.ContentType = resolveMultipartContentType(form, fileHeader)🤖 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/openai.go` around lines 2569 - 2574, The multipart content-type fallback logic is duplicated between the OpenAI upload path and the inference upload path, so extract it into a shared helper like resolveMultipartContentType(form, fileHeader) and use that from the upload handling code. Move the precedence rules currently in the upload flow into the helper so both callers rely on the same implementation and won’t diverge if the form-field vs multipart-header behavior changes later.
🤖 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.
Nitpick comments:
In `@transports/bifrost-http/integrations/openai.go`:
- Around line 2569-2574: The multipart content-type fallback logic is duplicated
between the OpenAI upload path and the inference upload path, so extract it into
a shared helper like resolveMultipartContentType(form, fileHeader) and use that
from the upload handling code. Move the precedence rules currently in the upload
flow into the helper so both callers rely on the same implementation and won’t
diverge if the form-field vs multipart-header behavior changes later.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 5ce23b05-ea69-4252-9b0d-22f487081007
📒 Files selected for processing (8)
core/providers/anthropic/anthropic.gocore/providers/anthropic/responses.gocore/providers/anthropic/types.gocore/providers/anthropic/utils.gocore/schemas/responses.gotransports/bifrost-http/handlers/inference.gotransports/bifrost-http/integrations/anthropic.gotransports/bifrost-http/integrations/openai.go
✅ Files skipped from review due to trivial changes (2)
- core/providers/anthropic/types.go
- core/providers/anthropic/responses.go
🚧 Files skipped from review as they are similar to previous changes (4)
- transports/bifrost-http/integrations/anthropic.go
- core/providers/anthropic/utils.go
- core/schemas/responses.go
- core/providers/anthropic/anthropic.go
64c64ce to
fc9dca1
Compare
fc9dca1 to
df83d64
Compare
df83d64 to
e717c35
Compare
aecdf1c to
68f1201
Compare
Merge activity
|
The base branch was changed.

Summary
This PR adds
content_typesupport to Anthropic file uploads and enablesfile_idreferences in document/image content blocks via the Files API beta header. Previously, file uploads always used the browser-inferred content type fromCreateFormFile, and there was no way to reference already-uploaded files by ID in message content.Changes
writer.CreateFormFilewith a manually constructedtextproto.MIMEHeaderinFileUpload, allowing the caller-suppliedContentTypeto be set on the multipart part rather than relying on the defaultapplication/octet-stream.ContentType *stringtoAnthropicFileUploadRequestandBifrostFileUploadRequestso the MIME type flows from the HTTP transport through to the provider.content_typeis now read from the multipart form field first, falling back to theContent-Typeheader on the file part itself."file"source case intoBifrostResponsesDocumentBlockto map Anthropicfile_idreferences back into the Bifrost schema.ConvertResponsesFileBlockToAnthropichandling forFileID-only blocks, settingsource.type = "file"and populatingfile_idbefore returning early.FileIDfromResponsesMessageContentBlocktoResponsesInputMessageContentBlockFile, where it semantically belongs alongsideFileData,FileURL, andFilename.files-api-2025-04-14beta header when any message content block contains a"file"source type, consistent with how other beta headers are appended.Type of change
Affected areas
How to test
go test ./...content_typeform field (e.g.,application/pdf) and verify the multipart part carries that content type.Content-Typeheader is set and confirm it is used as the fallback.file_idand confirm thefiles-api-2025-04-14beta header is automatically added to the outgoing request."file"source block is correctly mapped back toResponsesInputMessageContentBlockFile.FileID.Breaking changes
Related issues
Security considerations
No new auth surfaces or secrets handling introduced. The
ContentTypevalue is trimmed and validated before being set as a MIME header to avoid header injection.Checklist
docs/contributing/README.mdand followed the guidelines