diff --git a/coderd/x/chatd/chatprompt/chatprompt.go b/coderd/x/chatd/chatprompt/chatprompt.go index 126edf8dba..ac86b11e96 100644 --- a/coderd/x/chatd/chatprompt/chatprompt.go +++ b/coderd/x/chatd/chatprompt/chatprompt.go @@ -1588,6 +1588,7 @@ func partsToMessageParts( continue } result = append(result, fantasy.FilePart{ + Filename: name, Data: data, MediaType: mediaType, ProviderOptions: opts, diff --git a/coderd/x/chatd/chatprompt/chatprompt_test.go b/coderd/x/chatd/chatprompt/chatprompt_test.go index 8f66da7cec..895fe02aea 100644 --- a/coderd/x/chatd/chatprompt/chatprompt_test.go +++ b/coderd/x/chatd/chatprompt/chatprompt_test.go @@ -502,6 +502,7 @@ func TestConvertMessagesWithFiles_MixedResolvedAndMissingFilePartsInSingleMessag filePart, ok := fantasy.AsMessagePart[fantasy.FilePart](prompt[0].Content[0]) require.True(t, ok, "expected first part to stay a FilePart") + require.Equal(t, "resolved.png", filePart.Filename) require.Equal(t, resolvedData, filePart.Data) require.Equal(t, "image/png", filePart.MediaType) diff --git a/coderd/x/chatd/chatprovider/chatprovider_test.go b/coderd/x/chatd/chatprovider/chatprovider_test.go index 80911d89cd..261c6e9528 100644 --- a/coderd/x/chatd/chatprovider/chatprovider_test.go +++ b/coderd/x/chatd/chatprovider/chatprovider_test.go @@ -1401,10 +1401,11 @@ func TestModelFromConfig_ExtraHeaders(t *testing.T) { // path that lets a user-uploaded PDF actually reach Claude/Bedrock: a // fantasy.FilePart with MediaType "application/pdf" must be serialized as an // Anthropic "document" content block with a base64 source carrying the PDF -// bytes. Older fantasy versions silently dropped PDF FileParts in the -// Anthropic provider, so the user message ended up empty and the model never -// saw the document. See coder/fantasy#37 (cherry-pick of upstream -// charmbracelet/fantasy#197). The Generate call would fail outright on the +// bytes and a sanitized filename as the document title. Older fantasy versions +// silently dropped PDF FileParts in the Anthropic provider, so the user +// message ended up empty and the model never saw the document. The underlying +// PDF block support came from coder/fantasy#37, a cherry-pick of upstream +// charmbracelet/fantasy#197. The Generate call would fail outright on the // regressed code path because the dropped FilePart leaves the request with // zero messages. func TestModelFromConfig_AnthropicPDFFilePartReachesProvider(t *testing.T) { @@ -1423,6 +1424,7 @@ func TestModelFromConfig_AnthropicPDFFilePartReachesProvider(t *testing.T) { var blocks []struct { Type string `json:"type"` + Title string `json:"title"` Source struct { Type string `json:"type"` MediaType string `json:"media_type"` @@ -1439,6 +1441,11 @@ func TestModelFromConfig_AnthropicPDFFilePartReachesProvider(t *testing.T) { } assert.Equal(t, "base64", block.Source.Type, "PDF document block must use a base64 source") assert.Equal(t, wantData, block.Source.Data, "PDF bytes must round-trip base64 unchanged") + assert.Equal(t, + "quarterly report v1 pdf", + block.Title, + "PDF filename must reach Anthropic as a sanitized document title", + ) if block.Source.MediaType != "" { assert.Equal(t, "application/pdf", block.Source.MediaType) } @@ -1462,7 +1469,11 @@ func TestModelFromConfig_AnthropicPDFFilePartReachesProvider(t *testing.T) { { Role: fantasy.MessageRoleUser, Content: []fantasy.MessagePart{ - fantasy.FilePart{Data: pdfData, MediaType: "application/pdf"}, + fantasy.FilePart{ + Filename: "quarterly_report.v1.pdf", + Data: pdfData, + MediaType: "application/pdf", + }, }, }, }, diff --git a/go.mod b/go.mod index 95f96d276c..82bfbab673 100644 --- a/go.mod +++ b/go.mod @@ -94,7 +94,11 @@ replace github.com/spf13/afero => github.com/aslilac/afero v0.0.0-20250403163713 // emit a Base64 PDF document block for application/pdf FileParts on the // Anthropic provider so user-uploaded PDFs actually reach Claude/Bedrock // instead of being silently dropped. -// 11) coder/fantasy#39, support Anthropic thinking_display natively. +// 11) coder/fantasy#38, forward PDF and text filenames as a sanitized +// Anthropic document title so Claude can refer to attachments by +// name, and warn on unsupported FilePart media types instead of +// silently dropping them. +// 12) coder/fantasy#39, support Anthropic thinking_display natively. // See: https://github.com/coder/fantasy/commits/a2a3f2171ec8 replace charm.land/fantasy => github.com/coder/fantasy v0.0.0-20260604204802-a2a3f2171ec8