@@ -69,6 +69,12 @@ type AnthropicError struct {
69
69
} `json:"error"` // Error details
70
70
}
71
71
72
+ type AnthropicImageContent struct {
73
+ Type ImageContentType `json:"type"`
74
+ URL string `json:"url"`
75
+ MediaType string `json:"media_type,omitempty"`
76
+ }
77
+
72
78
// AnthropicProvider implements the Provider interface for Anthropic's Claude API.
73
79
type AnthropicProvider struct {
74
80
logger schemas.Logger // Logger for provider operations
@@ -255,8 +261,10 @@ func (provider *AnthropicProvider) TextCompletion(ctx context.Context, model, ke
255
261
{
256
262
Index : 0 ,
257
263
Message : schemas.BifrostMessage {
258
- Role : schemas .ModelChatMessageRoleAssistant ,
259
- Content : & response .Completion ,
264
+ Role : schemas .ModelChatMessageRoleAssistant ,
265
+ Content : schemas.MessageContent {
266
+ ContentStr : & response .Completion ,
267
+ },
260
268
},
261
269
},
262
270
}
@@ -318,22 +326,34 @@ func (provider *AnthropicProvider) ChatCompletion(ctx context.Context, model, ke
318
326
}
319
327
320
328
// buildAnthropicImageSourceMap creates the "source" map for an Anthropic image content part.
321
- func buildAnthropicImageSourceMap (imgContent * schemas.ImageContent ) map [string ]interface {} {
329
+ func buildAnthropicImageSourceMap (imgContent * schemas.ImageURLStruct ) map [string ]interface {} {
322
330
if imgContent == nil {
323
331
return nil
324
332
}
325
333
326
- formattedImgContent := * FormatImageContent (imgContent , false )
334
+ sanitizedURL , _ := SanitizeImageURL (imgContent .URL )
335
+ urlTypeInfo := ExtractURLTypeInfo (sanitizedURL )
336
+
337
+ formattedImgContent := AnthropicImageContent {
338
+ Type : urlTypeInfo .Type ,
339
+ MediaType : * urlTypeInfo .MediaType ,
340
+ }
341
+
342
+ if urlTypeInfo .DataURLWithoutPrefix != nil {
343
+ formattedImgContent .URL = * urlTypeInfo .DataURLWithoutPrefix
344
+ } else {
345
+ formattedImgContent .URL = sanitizedURL
346
+ }
327
347
328
348
sourceMap := map [string ]interface {}{
329
349
"type" : string (formattedImgContent .Type ), // "base64" or "url"
330
350
}
331
351
332
- if formattedImgContent .Type == schemas . ImageContentTypeURL {
352
+ if formattedImgContent .Type == ImageContentTypeURL {
333
353
sourceMap ["url" ] = formattedImgContent .URL
334
354
} else {
335
- if formattedImgContent .MediaType != nil {
336
- sourceMap ["media_type" ] = * formattedImgContent .MediaType
355
+ if formattedImgContent .MediaType != "" {
356
+ sourceMap ["media_type" ] = formattedImgContent .MediaType
337
357
}
338
358
sourceMap ["data" ] = formattedImgContent .URL // URL field contains base64 data string
339
359
}
@@ -345,10 +365,18 @@ func prepareAnthropicChatRequest(messages []schemas.BifrostMessage, params *sche
345
365
var systemMessages []BedrockAnthropicSystemMessage
346
366
for _ , msg := range messages {
347
367
if msg .Role == schemas .ModelChatMessageRoleSystem {
348
- if msg .Content != nil {
368
+ if msg .Content . ContentStr != nil {
349
369
systemMessages = append (systemMessages , BedrockAnthropicSystemMessage {
350
- Text : * msg .Content ,
370
+ Text : * msg .Content . ContentStr ,
351
371
})
372
+ } else if msg .Content .ContentBlocks != nil {
373
+ for _ , block := range * msg .Content .ContentBlocks {
374
+ if block .Text != nil {
375
+ systemMessages = append (systemMessages , BedrockAnthropicSystemMessage {
376
+ Text : * block .Text ,
377
+ })
378
+ }
379
+ }
352
380
}
353
381
}
354
382
}
@@ -367,61 +395,47 @@ func prepareAnthropicChatRequest(messages []schemas.BifrostMessage, params *sche
367
395
368
396
var toolCallResultContent []map [string ]interface {}
369
397
370
- if msg .Content != nil {
398
+ if msg .Content . ContentStr != nil {
371
399
toolCallResultContent = append (toolCallResultContent , map [string ]interface {}{
372
400
"type" : "text" ,
373
- "text" : * msg .Content ,
401
+ "text" : * msg .Content . ContentStr ,
374
402
})
375
- }
376
-
377
- if (msg .UserMessage != nil && msg .UserMessage .ImageContent != nil ) || (msg .ToolMessage != nil && msg .ToolMessage .ImageContent != nil ) {
378
- var messageImageContent schemas.ImageContent
379
- if msg .UserMessage != nil && msg .UserMessage .ImageContent != nil {
380
- // Create a copy to avoid modifying the original
381
- messageImageContent = * msg .UserMessage .ImageContent
382
- } else if msg .ToolMessage != nil && msg .ToolMessage .ImageContent != nil {
383
- // Create a copy to avoid modifying the original
384
- messageImageContent = * msg .ToolMessage .ImageContent
385
- }
386
-
387
- imageSource := buildAnthropicImageSourceMap (& messageImageContent )
388
- if imageSource != nil {
403
+ } else if msg .Content .ContentBlocks != nil {
404
+ for _ , block := range * msg .Content .ContentBlocks {
389
405
toolCallResultContent = append (toolCallResultContent , map [string ]interface {}{
390
- "type" : "image " ,
391
- "source " : imageSource ,
406
+ "type" : "text " ,
407
+ "text " : block . Text ,
392
408
})
393
409
}
394
410
}
395
411
396
412
toolCallResult ["content" ] = toolCallResultContent
397
-
398
413
content = append (content , toolCallResult )
399
414
} else {
400
- if (msg .UserMessage != nil && msg .UserMessage .ImageContent != nil ) || (msg .ToolMessage != nil && msg .ToolMessage .ImageContent != nil ) {
401
- var messageImageContent schemas.ImageContent
402
- if msg .UserMessage != nil && msg .UserMessage .ImageContent != nil {
403
- // Create a copy to avoid modifying the original
404
- messageImageContent = * msg .UserMessage .ImageContent
405
- } else if msg .ToolMessage != nil && msg .ToolMessage .ImageContent != nil {
406
- // Create a copy to avoid modifying the original
407
- messageImageContent = * msg .ToolMessage .ImageContent
408
- }
409
-
410
- imageSource := buildAnthropicImageSourceMap (& messageImageContent )
411
- if imageSource != nil {
412
- content = append (content , map [string ]interface {}{
413
- "type" : "image" ,
414
- "source" : imageSource ,
415
- })
416
- }
417
- }
418
-
419
415
// Add text content if present
420
- if msg .Content != nil && * msg .Content != "" {
416
+ if msg .Content . ContentStr != nil && * msg .Content . ContentStr != "" {
421
417
content = append (content , map [string ]interface {}{
422
418
"type" : "text" ,
423
- "text" : * msg .Content ,
419
+ "text" : * msg .Content . ContentStr ,
424
420
})
421
+ } else if msg .Content .ContentBlocks != nil {
422
+ for _ , block := range * msg .Content .ContentBlocks {
423
+ if block .Text != nil && * block .Text != "" {
424
+ content = append (content , map [string ]interface {}{
425
+ "type" : "text" ,
426
+ "text" : * block .Text ,
427
+ })
428
+ }
429
+ if block .ImageURL != nil {
430
+ imageSource := buildAnthropicImageSourceMap (block .ImageURL )
431
+ if imageSource != nil {
432
+ content = append (content , map [string ]interface {}{
433
+ "type" : "image" ,
434
+ "source" : imageSource ,
435
+ })
436
+ }
437
+ }
438
+ }
425
439
}
426
440
427
441
// Add thinking content if present in AssistantMessage
@@ -577,20 +591,20 @@ func prepareAnthropicChatRequest(messages []schemas.BifrostMessage, params *sche
577
591
578
592
func parseAnthropicResponse (response * AnthropicChatResponse , bifrostResponse * schemas.BifrostResponse ) (* schemas.BifrostResponse , * schemas.BifrostError ) {
579
593
// Collect all content and tool calls into a single message
580
- var content strings.Builder
581
594
var toolCalls []schemas.ToolCall
582
595
var thinking string
583
596
597
+ var contentBlocks []schemas.ContentBlock
584
598
// Process content and tool calls
585
599
for _ , c := range response .Content {
586
600
switch c .Type {
587
601
case "thinking" :
588
602
thinking = c .Thinking
589
603
case "text" :
590
- if content . Len () > 0 {
591
- content . WriteString ( " \n " )
592
- }
593
- content . WriteString ( c . Text )
604
+ contentBlocks = append ( contentBlocks , schemas. ContentBlock {
605
+ Type : "text" ,
606
+ Text : & c . Text ,
607
+ } )
594
608
case "tool_use" :
595
609
function := schemas.FunctionCall {
596
610
Name : & c .Name ,
@@ -612,7 +626,6 @@ func parseAnthropicResponse(response *AnthropicChatResponse, bifrostResponse *sc
612
626
}
613
627
614
628
// Create the assistant message
615
- messageContent := content .String ()
616
629
var assistantMessage * schemas.AssistantMessage
617
630
618
631
// Create AssistantMessage if we have tool calls or thinking
@@ -632,8 +645,10 @@ func parseAnthropicResponse(response *AnthropicChatResponse, bifrostResponse *sc
632
645
{
633
646
Index : 0 ,
634
647
Message : schemas.BifrostMessage {
635
- Role : schemas .ModelChatMessageRoleAssistant ,
636
- Content : & messageContent ,
648
+ Role : schemas .ModelChatMessageRoleAssistant ,
649
+ Content : schemas.MessageContent {
650
+ ContentBlocks : & contentBlocks ,
651
+ },
637
652
AssistantMessage : assistantMessage ,
638
653
},
639
654
FinishReason : & response .StopReason ,
0 commit comments