diff --git a/core/internal/testutil/account.go b/core/internal/testutil/account.go index 6c1d62b5c..3c53d84d4 100644 --- a/core/internal/testutil/account.go +++ b/core/internal/testutil/account.go @@ -51,6 +51,7 @@ type TestScenarios struct { FileRetrieve bool // File API retrieve functionality FileDelete bool // File API delete functionality FileContent bool // File API content download functionality + FileBatchInput bool // Whether batch create supports file-based input (InputFileID) } // ComprehensiveTestConfig extends TestConfig with additional scenarios diff --git a/core/internal/testutil/batch.go b/core/internal/testutil/batch.go index 58d61e521..fe28cb253 100644 --- a/core/internal/testutil/batch.go +++ b/core/internal/testutil/batch.go @@ -627,10 +627,10 @@ func RunFileUnsupportedTest(t *testing.T, client *bifrost.Bifrost, ctx context.C // RunFileAndBatchIntegrationTest tests the integration between file upload and batch create func RunFileAndBatchIntegrationTest(t *testing.T, client *bifrost.Bifrost, ctx context.Context, testConfig ComprehensiveTestConfig) { - // Skip if either file upload or batch create is not supported - if !testConfig.Scenarios.FileUpload || !testConfig.Scenarios.BatchCreate { - t.Logf("[SKIPPED] File and Batch Integration: FileUpload=%v, BatchCreate=%v for provider %s", - testConfig.Scenarios.FileUpload, testConfig.Scenarios.BatchCreate, testConfig.Provider) + // Skip if file-based batch input is not supported + if !testConfig.Scenarios.FileBatchInput { + t.Logf("[SKIPPED] File and Batch Integration: FileBatchInput=%v for provider %s", + testConfig.Scenarios.FileBatchInput, testConfig.Provider) return } diff --git a/core/internal/testutil/tests.go b/core/internal/testutil/tests.go index 218f3e869..f9a7401e5 100644 --- a/core/internal/testutil/tests.go +++ b/core/internal/testutil/tests.go @@ -115,7 +115,7 @@ func printTestSummary(t *testing.T, testConfig ComprehensiveTestConfig) { {"FileDelete", testConfig.Scenarios.FileDelete}, {"FileContent", testConfig.Scenarios.FileContent}, {"FileUnsupported", !testConfig.Scenarios.FileUpload && !testConfig.Scenarios.FileList && !testConfig.Scenarios.FileRetrieve && !testConfig.Scenarios.FileDelete && !testConfig.Scenarios.FileContent}, - {"FileAndBatchIntegration", testConfig.Scenarios.FileUpload && testConfig.Scenarios.BatchCreate}, + {"FileAndBatchIntegration", testConfig.Scenarios.FileBatchInput}, } supported := 0 diff --git a/core/providers/anthropic/anthropic_test.go b/core/providers/anthropic/anthropic_test.go index a50fc6b1c..615434ff7 100644 --- a/core/providers/anthropic/anthropic_test.go +++ b/core/providers/anthropic/anthropic_test.go @@ -57,7 +57,8 @@ func TestAnthropic(t *testing.T) { FileList: true, FileRetrieve: true, FileDelete: true, - FileContent: true, + FileContent: false, + FileBatchInput: false, // Anthropic batch API only supports inline requests, not file-based input }, } diff --git a/core/providers/bedrock/bedrock_test.go b/core/providers/bedrock/bedrock_test.go index 9ad550358..31c3fc227 100644 --- a/core/providers/bedrock/bedrock_test.go +++ b/core/providers/bedrock/bedrock_test.go @@ -79,6 +79,7 @@ func TestBedrock(t *testing.T) { FileRetrieve: true, FileDelete: true, FileContent: true, + FileBatchInput: true, }, } diff --git a/core/providers/gemini/batch.go b/core/providers/gemini/batch.go index bf02b0af2..c15a12208 100644 --- a/core/providers/gemini/batch.go +++ b/core/providers/gemini/batch.go @@ -64,34 +64,55 @@ func buildBatchRequestItems(requests []schemas.BatchRequestItem) []GeminiBatchRe requestData = req.Params } - // Extract messages from the request data + // Extract messages from the request data - handle multiple possible types + // Go type assertions don't work across slice types, so we handle each case if requestData != nil { - if msgs, ok := requestData["messages"].([]interface{}); ok { - for _, msg := range msgs { - if msgMap, ok := msg.(map[string]interface{}); ok { - role := "user" - if r, ok := msgMap["role"].(string); ok { - if r == "assistant" { - role = "model" - } else if r == "system" { - // System messages are handled separately in Gemini - continue - } else { - role = r - } - } + var messages []map[string]interface{} - parts := []*Part{} - if c, ok := msgMap["content"].(string); ok { - parts = append(parts, &Part{Text: c}) - } + // Try []interface{} first (generic JSON unmarshaling) + if msgsInterface, ok := requestData["messages"].([]interface{}); ok { + for _, m := range msgsInterface { + if msgMap, ok := m.(map[string]interface{}); ok { + messages = append(messages, msgMap) + } + } + } else if msgsTyped, ok := requestData["messages"].([]map[string]interface{}); ok { + // Try []map[string]interface{} (typed maps) + messages = msgsTyped + } else if msgsString, ok := requestData["messages"].([]map[string]string); ok { + // Try []map[string]string (test case format) + for _, m := range msgsString { + msgMap := make(map[string]interface{}) + for k, v := range m { + msgMap[k] = v + } + messages = append(messages, msgMap) + } + } - contents = append(contents, Content{ - Role: role, - Parts: parts, - }) + // Process extracted messages + for _, msgMap := range messages { + role := "user" + if r, ok := msgMap["role"].(string); ok { + if r == "assistant" { + role = "model" + } else if r == "system" { + // System messages are handled separately in Gemini + continue + } else { + role = r } } + + parts := []*Part{} + if c, ok := msgMap["content"].(string); ok { + parts = append(parts, &Part{Text: c}) + } + + contents = append(contents, Content{ + Role: role, + Parts: parts, + }) } } diff --git a/core/providers/gemini/gemini_test.go b/core/providers/gemini/gemini_test.go index a39645016..3498f5e36 100644 --- a/core/providers/gemini/gemini_test.go +++ b/core/providers/gemini/gemini_test.go @@ -63,7 +63,8 @@ func TestGemini(t *testing.T) { FileList: true, FileRetrieve: true, FileDelete: true, - FileContent: true, + FileContent: false, + FileBatchInput: true, }, } diff --git a/core/providers/openai/openai_test.go b/core/providers/openai/openai_test.go index ff40595b1..19077b809 100644 --- a/core/providers/openai/openai_test.go +++ b/core/providers/openai/openai_test.go @@ -70,6 +70,7 @@ func TestOpenAI(t *testing.T) { FileRetrieve: true, FileDelete: true, FileContent: true, + FileBatchInput: true, }, }