Repository: danielmiessler/fabric
File: internal/plugins/ai/anthropic/anthropic.go (Go)

Code before:
		}
		if strings.EqualFold(mimeType, "application/pdf") {
			return anthropic.NewDocumentBlock(anthropic.Base64PDFSourceParam{Data: data}), true
		}
		if normalized := normalizeImageMimeType(mimeType); normalized != "" {
			return anthropic.NewImageBlockBase64(normalized, data), true
		}
		return anthropic.ContentBlockParamUnion{}, false
	}
	if isPDFURL(url) {
		return anthropic.NewDocumentBlock(anthropic.URLPDFSourceParam{URL: url}), true
	}
	return anthropic.NewImageBlock(anthropic.URLImageSourceParam{URL: url}), true
}

func parseDataURL(value string) (mimeType string, data string, ok bool) {
	if !strings.HasPrefix(value, "data:") {
		return "", "", false
	}
	withoutPrefix := strings.TrimPrefix(value, "data:")
	parts := strings.SplitN(withoutPrefix, ",", 2)
	if len(parts) != 2 {
		return "", "", false
	}
	meta := strings.TrimSpace(parts[0])
	data = strings.TrimSpace(parts[1])
	if data == "" {
		return "", "", false
	}
	metaParts := strings.Split(meta, ";")
	mimeType = strings.TrimSpace(metaParts[0])
	if mimeType == "" {
		return "", "", false
	}
	hasBase64 := false
	for _, part := range metaParts[1:] {
		if strings.EqualFold(strings.TrimSpace(part), "base64") {
			hasBase64 = true
			break
		}
	}
	if !hasBase64 {
		return "", "", false
	}
	return mimeType, data, true
}

func normalizeImageMimeType(mimeType string) string {
	normalized := strings.ToLower(strings.TrimSpace(mimeType))
	switch normalized {
	case "image/jpg":

Review comment (bug):
Good suggestion! Added debug logging for both failed data URL parsing and unsupported MIME types. This follows the existing debug logging pattern in this file and will help users troubleshoot attachment issues.

Code after:

// contentBlockFromAttachmentURL converts an attachment URL into an Anthropic content block.
// For data URLs, it parses the MIME type and base64 data to create image or PDF blocks.
// For remote URLs, it creates URL-based image blocks, or PDF document blocks if the URL ends in .pdf.
// Returns the content block and true on success, or an empty block and false if unsupported.
func contentBlockFromAttachmentURL(url string) (anthropic.ContentBlockParamUnion, bool) {
	if strings.HasPrefix(url, "data:") {
		mimeType, data, ok := parseDataURL(url)
		if !ok {
			debuglog.Debug(debuglog.Basic, "contentBlockFromAttachmentURL: failed to parse data URL")
			return anthropic.ContentBlockParamUnion{}, false
		}
		if strings.EqualFold(mimeType, "application/pdf") {
			return anthropic.NewDocumentBlock(anthropic.Base64PDFSourceParam{Data: data}), true
		}
		if normalized := normalizeImageMimeType(mimeType); normalized != "" {
			return anthropic.NewImageBlockBase64(normalized, data), true
		}
		debuglog.Debug(debuglog.Basic, "contentBlockFromAttachmentURL: unsupported MIME type %s", mimeType)
		return anthropic.ContentBlockParamUnion{}, false
	}
	if isPDFURL(url) {
		return anthropic.NewDocumentBlock(anthropic.URLPDFSourceParam{URL: url}), true
	}
	return anthropic.NewImageBlock(anthropic.URLImageSourceParam{URL: url}), true
}

// parseDataURL parses an RFC 2397 data URL, extracting the MIME type and base64-encoded data.
// Only base64-encoded data URLs are supported; URL-encoded data URLs will return ok=false.
func parseDataURL(value string) (mimeType string, data string, ok bool) {
	if !strings.HasPrefix(value, "data:") {
		return "", "", false
	}
	withoutPrefix := strings.TrimPrefix(value, "data:")
	parts := strings.SplitN(withoutPrefix, ",", 2)
	if len(parts) != 2 {
		return "", "", false
	}
	meta := strings.TrimSpace(parts[0])
	data = strings.TrimSpace(parts[1])
	if data == "" {
		return "", "", false
	}
	metaParts := strings.Split(meta, ";")
	mimeType = strings.TrimSpace(metaParts[0])
	if mimeType == "" {
		return "", "", false
	}
	hasBase64 := false
	for _, part := range metaParts[1:] {
		if strings.EqualFold(strings.TrimSpace(part), "base64") {