Skip to content

Commit 05c93c4

Browse files
refactor: changed content schema to follow openai
1 parent bc74f7d commit 05c93c4

File tree

12 files changed

+742
-438
lines changed

12 files changed

+742
-438
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,12 @@ For additional configurations in HTTP server setup, please read [this](https://g
149149
Provider: schemas.OpenAI,
150150
Model: "gpt-4o-mini", // make sure you have configured gpt-4o-mini in your account interface
151151
Input: schemas.RequestInput{
152-
ChatCompletionInput: bifrost.Ptr([]schemas.Message{{
153-
Role: schemas.RoleUser,
154-
Content: bifrost.Ptr("What is a LLM gateway?"),
155-
}}),
152+
ChatCompletionInput: bifrost.Ptr([]schemas.BifrostMessage{{
153+
Role: schemas.ModelChatMessageRoleUser,
154+
Content: schemas.MessageContent{
155+
ContentStr: bifrost.Ptr("What is a LLM gateway?"),
156+
},
157+
}}),
156158
},
157159
},
158160
)

core/providers/anthropic.go

Lines changed: 72 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ type AnthropicError struct {
6969
} `json:"error"` // Error details
7070
}
7171

72+
type AnthropicImageContent struct {
73+
Type ImageContentType `json:"type"`
74+
URL string `json:"url"`
75+
MediaType string `json:"media_type,omitempty"`
76+
}
77+
7278
// AnthropicProvider implements the Provider interface for Anthropic's Claude API.
7379
type AnthropicProvider struct {
7480
logger schemas.Logger // Logger for provider operations
@@ -255,8 +261,10 @@ func (provider *AnthropicProvider) TextCompletion(ctx context.Context, model, ke
255261
{
256262
Index: 0,
257263
Message: schemas.BifrostMessage{
258-
Role: schemas.ModelChatMessageRoleAssistant,
259-
Content: &response.Completion,
264+
Role: schemas.ModelChatMessageRoleAssistant,
265+
Content: schemas.MessageContent{
266+
ContentStr: &response.Completion,
267+
},
260268
},
261269
},
262270
}
@@ -318,22 +326,34 @@ func (provider *AnthropicProvider) ChatCompletion(ctx context.Context, model, ke
318326
}
319327

320328
// 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{} {
322330
if imgContent == nil {
323331
return nil
324332
}
325333

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+
}
327347

328348
sourceMap := map[string]interface{}{
329349
"type": string(formattedImgContent.Type), // "base64" or "url"
330350
}
331351

332-
if formattedImgContent.Type == schemas.ImageContentTypeURL {
352+
if formattedImgContent.Type == ImageContentTypeURL {
333353
sourceMap["url"] = formattedImgContent.URL
334354
} else {
335-
if formattedImgContent.MediaType != nil {
336-
sourceMap["media_type"] = *formattedImgContent.MediaType
355+
if formattedImgContent.MediaType != "" {
356+
sourceMap["media_type"] = formattedImgContent.MediaType
337357
}
338358
sourceMap["data"] = formattedImgContent.URL // URL field contains base64 data string
339359
}
@@ -345,10 +365,18 @@ func prepareAnthropicChatRequest(messages []schemas.BifrostMessage, params *sche
345365
var systemMessages []BedrockAnthropicSystemMessage
346366
for _, msg := range messages {
347367
if msg.Role == schemas.ModelChatMessageRoleSystem {
348-
if msg.Content != nil {
368+
if msg.Content.ContentStr != nil {
349369
systemMessages = append(systemMessages, BedrockAnthropicSystemMessage{
350-
Text: *msg.Content,
370+
Text: *msg.Content.ContentStr,
351371
})
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+
}
352380
}
353381
}
354382
}
@@ -367,61 +395,47 @@ func prepareAnthropicChatRequest(messages []schemas.BifrostMessage, params *sche
367395

368396
var toolCallResultContent []map[string]interface{}
369397

370-
if msg.Content != nil {
398+
if msg.Content.ContentStr != nil {
371399
toolCallResultContent = append(toolCallResultContent, map[string]interface{}{
372400
"type": "text",
373-
"text": *msg.Content,
401+
"text": *msg.Content.ContentStr,
374402
})
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 {
389405
toolCallResultContent = append(toolCallResultContent, map[string]interface{}{
390-
"type": "image",
391-
"source": imageSource,
406+
"type": "text",
407+
"text": block.Text,
392408
})
393409
}
394410
}
395411

396412
toolCallResult["content"] = toolCallResultContent
397-
398413
content = append(content, toolCallResult)
399414
} 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-
419415
// Add text content if present
420-
if msg.Content != nil && *msg.Content != "" {
416+
if msg.Content.ContentStr != nil && *msg.Content.ContentStr != "" {
421417
content = append(content, map[string]interface{}{
422418
"type": "text",
423-
"text": *msg.Content,
419+
"text": *msg.Content.ContentStr,
424420
})
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+
}
425439
}
426440

427441
// Add thinking content if present in AssistantMessage
@@ -577,20 +591,20 @@ func prepareAnthropicChatRequest(messages []schemas.BifrostMessage, params *sche
577591

578592
func parseAnthropicResponse(response *AnthropicChatResponse, bifrostResponse *schemas.BifrostResponse) (*schemas.BifrostResponse, *schemas.BifrostError) {
579593
// Collect all content and tool calls into a single message
580-
var content strings.Builder
581594
var toolCalls []schemas.ToolCall
582595
var thinking string
583596

597+
var contentBlocks []schemas.ContentBlock
584598
// Process content and tool calls
585599
for _, c := range response.Content {
586600
switch c.Type {
587601
case "thinking":
588602
thinking = c.Thinking
589603
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+
})
594608
case "tool_use":
595609
function := schemas.FunctionCall{
596610
Name: &c.Name,
@@ -612,7 +626,6 @@ func parseAnthropicResponse(response *AnthropicChatResponse, bifrostResponse *sc
612626
}
613627

614628
// Create the assistant message
615-
messageContent := content.String()
616629
var assistantMessage *schemas.AssistantMessage
617630

618631
// Create AssistantMessage if we have tool calls or thinking
@@ -632,8 +645,10 @@ func parseAnthropicResponse(response *AnthropicChatResponse, bifrostResponse *sc
632645
{
633646
Index: 0,
634647
Message: schemas.BifrostMessage{
635-
Role: schemas.ModelChatMessageRoleAssistant,
636-
Content: &messageContent,
648+
Role: schemas.ModelChatMessageRoleAssistant,
649+
Content: schemas.MessageContent{
650+
ContentBlocks: &contentBlocks,
651+
},
637652
AssistantMessage: assistantMessage,
638653
},
639654
FinishReason: &response.StopReason,

core/providers/azure.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,10 @@ func (provider *AzureProvider) TextCompletion(ctx context.Context, model, key, t
269269
choices = append(choices, schemas.BifrostResponseChoice{
270270
Index: 0,
271271
Message: schemas.BifrostMessage{
272-
Role: schemas.ModelChatMessageRoleAssistant,
273-
Content: &textCopy,
272+
Role: schemas.ModelChatMessageRoleAssistant,
273+
Content: schemas.MessageContent{
274+
ContentStr: &textCopy,
275+
},
274276
},
275277
FinishReason: response.Choices[0].FinishReason,
276278
LogProbs: &schemas.LogProbs{

0 commit comments

Comments
 (0)