Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions bifrost.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,16 +180,16 @@ func (bifrost *Bifrost) processRequests(provider interfaces.Provider, queue chan
}

if req.Type == TextCompletionRequest {
if req.Input.TextInput == nil {
if req.Input.TextCompletionInput == nil {
err = fmt.Errorf("text not provided for text completion request")
} else {
result, err = provider.TextCompletion(req.Model, key, *req.Input.TextInput, req.Params)
result, err = provider.TextCompletion(req.Model, key, *req.Input.TextCompletionInput, req.Params)
}
} else if req.Type == ChatCompletionRequest {
if req.Input.ChatInput == nil {
if req.Input.ChatCompletionInput == nil {
err = fmt.Errorf("chats not provided for chat completion request")
} else {
result, err = provider.ChatCompletion(req.Model, key, *req.Input.ChatInput, req.Params)
result, err = provider.ChatCompletion(req.Model, key, *req.Input.ChatCompletionInput, req.Params)
}
}

Expand Down
11 changes: 6 additions & 5 deletions interfaces/bifrost.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ const (
//* Request Structs

type RequestInput struct {
TextInput *string
ChatInput *[]Message
TextCompletionInput *string
ChatCompletionInput *[]Message
}

type BifrostRequest struct {
Expand Down Expand Up @@ -109,9 +109,10 @@ type Message struct {
}

type ImageContent struct {
Type string `json:"type"`
URL string `json:"url"`
MediaType string `json:"media_type"`
Type *string `json:"type"`
URL string `json:"url"`
MediaType *string `json:"media_type"`
Detail *string `json:"detail"`
}

//* Response Structs
Expand Down
42 changes: 38 additions & 4 deletions providers/anthropic.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,44 @@ func (provider *AnthropicProvider) ChatCompletion(model, key string, messages []
// Format messages for Anthropic API
var formattedMessages []map[string]interface{}
for _, msg := range messages {
formattedMessages = append(formattedMessages, map[string]interface{}{
"role": msg.Role,
"content": msg.Content,
})
if msg.ImageContent != nil {
var content []map[string]interface{}

imageContent := map[string]interface{}{
"type": "image",
"source": map[string]interface{}{
"type": msg.ImageContent.Type,
},
}

// Handle different image source types
if *msg.ImageContent.Type == "url" {
imageContent["source"].(map[string]interface{})["url"] = msg.ImageContent.URL
} else {
imageContent["source"].(map[string]interface{})["media_type"] = msg.ImageContent.MediaType
imageContent["source"].(map[string]interface{})["data"] = msg.ImageContent.URL
}

content = append(content, imageContent)

// Add text content if present
if msg.Content != nil {
content = append(content, map[string]interface{}{
"type": "text",
"text": msg.Content,
})
}

formattedMessages = append(formattedMessages, map[string]interface{}{
"role": msg.Role,
"content": content,
})
} else {
formattedMessages = append(formattedMessages, map[string]interface{}{
"role": msg.Role,
"content": msg.Content,
})
}
}

preparedParams := PrepareParams(params)
Expand Down
20 changes: 12 additions & 8 deletions providers/bedrock.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,17 @@ type BedrockMistralChatMessage struct {
}

type BedrockAnthropicImageMessage struct {
Type string `json:"type"`
Type string `json:"type"`
Image BedrockAnthropicImage `json:"image"`
}

type BedrockAnthropicImage struct {
Format string `json:"string"`
Source BedrockAnthropicImageSource `json:"source"`
}

type BedrockAnthropicImageSource struct {
Type string `json:"type"`
MediaType string `json:"media_type"`
Data string `json:"data"`
Bytes string `json:"bytes"`
}

type BedrockMistralToolCall struct {
Expand Down Expand Up @@ -245,10 +248,11 @@ func (provider *BedrockProvider) PrepareChatCompletionMessages(messages []interf
} else if msg.ImageContent != nil {
content = BedrockAnthropicImageMessage{
Type: "image",
Source: BedrockAnthropicImageSource{
Type: msg.ImageContent.Type,
MediaType: msg.ImageContent.MediaType,
Data: msg.ImageContent.URL,
Image: BedrockAnthropicImage{
Format: *msg.ImageContent.Type,
Source: BedrockAnthropicImageSource{
Bytes: msg.ImageContent.URL,
},
},
}
}
Expand Down
45 changes: 34 additions & 11 deletions providers/openai.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,49 @@ func (provider *OpenAIProvider) TextCompletion(model, key, text string, params *

func (provider *OpenAIProvider) ChatCompletion(model, key string, messages []interfaces.Message, params *interfaces.ModelParameters) (*interfaces.BifrostResponse, error) {
// Format messages for OpenAI API
var openAIMessages []map[string]interface{}
var formattedMessages []map[string]interface{}
for _, msg := range messages {
var content any
if msg.Content != nil {
content = msg.Content
if msg.ImageContent != nil {
var content []map[string]interface{}

// Add text content if present
if msg.Content != nil {
content = append(content, map[string]interface{}{
"type": "text",
"text": msg.Content,
})
}

imageContent := map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": msg.ImageContent.URL,
},
}

if msg.ImageContent.Detail != nil {
imageContent["image_url"].(map[string]interface{})["detail"] = msg.ImageContent.Detail
}

content = append(content, imageContent)

formattedMessages = append(formattedMessages, map[string]interface{}{
"role": msg.Role,
"content": content,
})
} else {
content = msg.ImageContent
formattedMessages = append(formattedMessages, map[string]interface{}{
"role": msg.Role,
"content": msg.Content,
})
}

openAIMessages = append(openAIMessages, map[string]interface{}{
"role": msg.Role,
"content": content,
})
}

preparedParams := PrepareParams(params)

requestBody := MergeConfig(map[string]interface{}{
"model": model,
"messages": openAIMessages,
"messages": formattedMessages,
}, preparedParams)

jsonBody, err := json.Marshal(requestBody)
Expand Down
2 changes: 1 addition & 1 deletion tests/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (baseAccount *BaseAccount) GetKeysForProvider(providerKey interfaces.Suppor
return []interfaces.Key{
{
Value: os.Getenv("OPEN_AI_API_KEY"),
Models: []string{"gpt-4o-mini"},
Models: []string{"gpt-4o-mini", "gpt-4-turbo"},
Weight: 1.0,
},
}, nil
Expand Down
75 changes: 72 additions & 3 deletions tests/anthropic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

"github.com/maximhq/bifrost"
"github.com/maximhq/bifrost/interfaces"

"github.com/maximhq/maxim-go"
)

// setupAnthropicRequests sends multiple test requests to Anthropic
Expand All @@ -27,7 +29,7 @@ func setupAnthropicRequests(bifrost *bifrost.Bifrost) {
result, err := bifrost.TextCompletionRequest(interfaces.Anthropic, &interfaces.BifrostRequest{
Model: "claude-2.1",
Input: interfaces.RequestInput{
TextInput: &text,
TextCompletionInput: &text,
},
Params: &params,
}, ctx)
Expand Down Expand Up @@ -58,7 +60,7 @@ func setupAnthropicRequests(bifrost *bifrost.Bifrost) {
result, err := bifrost.ChatCompletionRequest(interfaces.Anthropic, &interfaces.BifrostRequest{
Model: "claude-3-7-sonnet-20250219",
Input: interfaces.RequestInput{
ChatInput: &messages,
ChatCompletionInput: &messages,
},
Params: &params,
}, ctx)
Expand All @@ -71,10 +73,77 @@ func setupAnthropicRequests(bifrost *bifrost.Bifrost) {
}(message, delay, i)
}

// Image input tests
setupAnthropicImageTests(bifrost, ctx)

// Tool calls test
setupAnthropicToolCalls(bifrost, ctx)
}

// setupAnthropicImageTests tests Anthropic's image input capabilities
func setupAnthropicImageTests(bifrost *bifrost.Bifrost, ctx context.Context) {
// Test with URL image
urlImageMessages := []interfaces.Message{
{
Role: interfaces.RoleUser,
Content: maxim.StrPtr("What is Happening in this picture?"),
ImageContent: &interfaces.ImageContent{
Type: maxim.StrPtr("url"),
URL: "https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg",
},
},
}

maxTokens := 4096

params := interfaces.ModelParameters{
MaxTokens: &maxTokens,
}

go func() {
result, err := bifrost.ChatCompletionRequest(interfaces.Anthropic, &interfaces.BifrostRequest{
Model: "claude-3-7-sonnet-20250219",
Input: interfaces.RequestInput{
ChatCompletionInput: &urlImageMessages,
},
Params: &params,
}, ctx)
if err != nil {
fmt.Printf("Error in Anthropic URL image request: %v\n", err)
} else {
fmt.Printf("🐒 URL Image Result: %s\n", result.Choices[0].Message.Content)
}
}()

// Test with base64 image
base64ImageMessages := []interfaces.Message{
{
Role: interfaces.RoleUser,
Content: maxim.StrPtr("What is this image about?"),
ImageContent: &interfaces.ImageContent{
Type: maxim.StrPtr("base64"),
URL: "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=",
MediaType: maxim.StrPtr("image/jpeg"),
},
},
}

go func() {
result, err := bifrost.ChatCompletionRequest(interfaces.Anthropic, &interfaces.BifrostRequest{
Model: "claude-3-7-sonnet-20250219",
Input: interfaces.RequestInput{
ChatCompletionInput: &base64ImageMessages,
},
Params: &params,
}, ctx)
if err != nil {
fmt.Printf("Error in Anthropic base64 image request: %v\n", err)
} else {
fmt.Printf("🐒 Base64 Image Result: %s\n", result.Choices[0].Message.Content)
}
}()
}

// setupAnthropicToolCalls tests Anthropic's function calling capability
func setupAnthropicToolCalls(bifrost *bifrost.Bifrost, ctx context.Context) {
anthropicMessages := []string{
Expand Down Expand Up @@ -121,7 +190,7 @@ func setupAnthropicToolCalls(bifrost *bifrost.Bifrost, ctx context.Context) {
result, err := bifrost.ChatCompletionRequest(interfaces.Anthropic, &interfaces.BifrostRequest{
Model: "claude-3-7-sonnet-20250219",
Input: interfaces.RequestInput{
ChatInput: &messages,
ChatCompletionInput: &messages,
},
Params: &params,
}, ctx)
Expand Down
Loading