Skip to content

Forward a data: URIContent to Anthropic as an inline base64 block - #939

Open
PratikDhanave (PratikDhanave) wants to merge 1 commit into
microsoft:mainfrom
PratikDhanaveFork:anthropic-datauri-uricontent-base64
Open

Forward a data: URIContent to Anthropic as an inline base64 block#939
PratikDhanave (PratikDhanave) wants to merge 1 commit into
microsoft:mainfrom
PratikDhanaveFork:anthropic-datauri-uricontent-base64

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

Problem

In provider/anthropicprovider/agent.go, buildMessageParam's *message.URIContent case handles image and PDF only as URL sources (anthropic.URLImageSourceParam / URLPDFSourceParam), which require an external http(s) reference. A URIContent carrying a data: URI is sent with the entire data:image/png;base64,... string as the url, which Anthropic rejects (400 invalid image source). The identical payload as DataContent succeeds — a cross-provider and intra-file inconsistency.

Fix

Detect a data: URI, decode it with message.DecodeDataURI, and emit an inline base64 image/PDF block using the same primitives as the DataContent branch (anthropic.NewImageBlockBase64 / anthropic.Base64PDFSourceParam). Non-data: http(s) URLs keep the URL source.

This mirrors the sibling providers that already special-case data: URIs on URIContent: Gemini (data:InlineData) and OpenAI chat (data: → inline DataContent mapping).

Test

TestBuildMessageParam_DataURIImageForwardedAsBase64 sends a URIContent with a data:image/png;base64,... URI and asserts the outgoing content block is a base64 image source. Fails before the fix (source.type == "url" with the data URI), passes after.

buildMessageParam's URIContent case only handled image and PDF as URL
sources (anthropic.URLImageSourceParam / URLPDFSourceParam), which require
an external http(s) reference. A URIContent carrying a data: URI was sent
with the whole data: string as the url, which Anthropic rejects (400).

Decode the data: URI and emit a base64 image/PDF block instead, reusing the
same primitives as the DataContent branch (NewImageBlockBase64 /
Base64PDFSourceParam). This mirrors the Gemini provider (data: -> InlineData)
and the OpenAI chat provider (data: -> inline), which already special-case
data: URIs on URIContent. Non-data http(s) URLs keep the URL source.
@PratikDhanave
PratikDhanave (PratikDhanave) requested a review from a team as a code owner August 28, 2026 08:41
Copilot AI lite review requested due to automatic review settings August 28, 2026 08:41
@github-actions github-actions Bot added area:provider Changes files in the provider area area:provider/anthropic Changes files in the provider / anthropic area size:medium At most 100 changed lines across at most 5 files pending-auto-risk Automatic risk classification is in progress labels Aug 28, 2026

Copilot AI 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.

Pull request overview

Fixes Anthropic provider handling of message.URIContent that contains data: URIs by converting them into inline base64 image/PDF blocks (instead of incorrectly sending the full data: URI as a URL source, which Anthropic rejects).

Changes:

  • Detect data: URIs in buildMessageParam and map them to Anthropic base64 image/PDF content blocks.
  • Add a regression test asserting data:-URI URIContent images are forwarded as base64 sources.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
provider/anthropicprovider/agent.go Adds data: URI detection/decoding and forwards inline content as base64 blocks for Anthropic.
provider/anthropicprovider/agent_test.go Adds a regression test for data: URI image forwarding behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +695 to +711
case strings.HasPrefix(strings.ToLower(c.URI), "data:"):
// A data: URI carries the bytes inline. Anthropic's URL image/PDF
// sources require an external http(s) reference, so a data: URI sent
// as a url source is rejected; decode it and send a base64 block
// instead, mirroring the DataContent branch and the Gemini/OpenAI
// data: handling.
data, mediaType, err := message.DecodeDataURI(c.URI)
if err != nil {
break
}
encoded := base64.StdEncoding.EncodeToString(data)
switch {
case strings.HasPrefix(mediaType, "image/"):
content = append(content, anthropic.NewImageBlockBase64(mediaType, encoded))
case isPDFMediaType(mediaType):
content = append(content, anthropic.NewDocumentBlock(anthropic.Base64PDFSourceParam{Data: encoded}))
}
for _, b := range blocks {
block, _ := b.(map[string]any)
source, _ := block["source"].(map[string]any)
if block["type"] == "image" && source["type"] == "base64" && source["media_type"] == "image/png" {
@github-actions

Copy link
Copy Markdown
Contributor

Scope: user-visible behavior (internal-only implementation change; no exported API surface changed)
Changed Go contract: buildMessageParam in provider/anthropicprovider*message.URIContent case now detects a (redacted) URI prefix, decodes it via message.DecodeDataURI, and emits an inline base64image/PDF block instead of passing the raw(redacted) string as a URL source (which Anthropic rejects with HTTP 400).
Upstream evidence reviewed:

  • Python: python/packages/anthropic/agent_framework_anthropic/_chat_client.py lines 883–889 — the 'uri' case unconditionally emits {"type": "url", "url": content.uri} without a (redacted) special-case. This is not a divergence: Python's content model separates 'data'(inline bytes) from'uri'(external references) at the type level, so a(redacted) URI would arrive as a 'data' content instance, not 'uri'. Go's message.URIContent is a unified type that can carry either form, requiring disambiguation at serialization time.
  • .NET: dotnet/src/Microsoft.Agents.AI.Anthropic/AnthropicClientExtensions.cs — no equivalent content-conversion logic visible; the extension layer delegates to the Anthropic SDK directly. No conflict found.

Result: ✅ Parity approved — no exported API changes. The fix closes a Go-specific bug caused by the language's unified URIContent type, and aligns the Anthropic provider with sibling Gemini and OpenAI providers already in this repo ((redacted) URI → inline base64 block). The upstream Python separation of datavsuri` content types makes this a Go-specific adaptation, not a divergence.

Generated by Go API Consistency Review Agent · sonnet46 · 35 AIC · ⌖ 5.07 AIC · ⊞ 6.4K ·

@github-actions github-actions Bot added parity-approved Go API consistency review found no parity issues risk:medium Contained production impact requiring normal review depth and removed pending-auto-risk Automatic risk classification is in progress labels Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider/anthropic Changes files in the provider / anthropic area area:provider Changes files in the provider area parity-approved Go API consistency review found no parity issues risk:medium Contained production impact requiring normal review depth size:medium At most 100 changed lines across at most 5 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants