From 8dd6dfc440695816e721faedfeceb43ad0dc169d Mon Sep 17 00:00:00 2001 From: akshaydeo Date: Sat, 27 Jun 2026 11:22:55 +0530 Subject: [PATCH] chunking_strategy as extra params for openai models --- core/providers/openai/transcription.go | 31 +++++++ core/providers/openai/transcription_test.go | 93 +++++++++++++++++++ .../bifrost-http/integrations/openai.go | 16 ++++ 3 files changed, 140 insertions(+) diff --git a/core/providers/openai/transcription.go b/core/providers/openai/transcription.go index cbfb130714..e0b1081797 100644 --- a/core/providers/openai/transcription.go +++ b/core/providers/openai/transcription.go @@ -3,6 +3,7 @@ package openai import ( "fmt" "mime/multipart" + "sort" "github.com/maximhq/bifrost/core/providers/utils" "github.com/maximhq/bifrost/core/schemas" @@ -40,6 +41,7 @@ func ToOpenAITranscriptionRequest(bifrostReq *schemas.BifrostTranscriptionReques if params != nil { openaiReq.TranscriptionParameters = *params + openaiReq.ExtraParams = params.ExtraParams } return openaiReq @@ -95,6 +97,35 @@ func ParseTranscriptionFormDataBodyFromRequest(writer *multipart.Writer, openaiR } } + // Forward provider-specific passthrough params (e.g. chunking_strategy, required by + // OpenAI diarization models). String values are written verbatim; object values are + // encoded as JSON since multipart form fields are strings. Keys are sorted so the + // emitted form is deterministic. + if len(openaiReq.ExtraParams) > 0 { + extraKeys := make([]string, 0, len(openaiReq.ExtraParams)) + for key := range openaiReq.ExtraParams { + extraKeys = append(extraKeys, key) + } + sort.Strings(extraKeys) + for _, key := range extraKeys { + value := openaiReq.ExtraParams[key] + var fieldValue string + switch v := value.(type) { + case string: + fieldValue = v + default: + encoded, err := schemas.MarshalSorted(v) + if err != nil { + return utils.NewBifrostOperationError(fmt.Sprintf("failed to encode %s field", key), err) + } + fieldValue = string(encoded) + } + if err := writer.WriteField(key, fieldValue); err != nil { + return utils.NewBifrostOperationError(fmt.Sprintf("failed to write %s field", key), err) + } + } + } + // Add file field last so large multipart uploads don't block model discovery upstream. filename := openaiReq.Filename if filename == "" { diff --git a/core/providers/openai/transcription_test.go b/core/providers/openai/transcription_test.go index ddbb33b888..1def4c5c4b 100644 --- a/core/providers/openai/transcription_test.go +++ b/core/providers/openai/transcription_test.go @@ -2,6 +2,7 @@ package openai import ( "bytes" + "encoding/json" "io" "mime" "mime/multipart" @@ -77,3 +78,95 @@ func TestParseTranscriptionFormDataBodyFromRequest_OrdersMetadataBeforeFile(t *t t.Fatalf("expected model part first, got order %v", order) } } + +// multipartFieldValue returns the value of the first multipart part with the +// given name, or "" if absent. +func multipartFieldValue(t *testing.T, contentType string, body []byte, name string) string { + t.Helper() + _, params, err := mime.ParseMediaType(contentType) + if err != nil { + t.Fatalf("ParseMediaType(%q): %v", contentType, err) + } + boundary := params["boundary"] + reader := multipart.NewReader(bytes.NewReader(body), boundary) + for { + part, err := reader.NextPart() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("NextPart(): %v", err) + } + if part.FormName() == name { + data, _ := io.ReadAll(part) + _ = part.Close() + return string(data) + } + _, _ = io.Copy(io.Discard, part) + _ = part.Close() + } + return "" +} + +func TestParseTranscriptionFormDataBodyFromRequest_ChunkingStrategyString(t *testing.T) { + var body bytes.Buffer + writer := multipart.NewWriter(&body) + req := &OpenAITranscriptionRequest{ + Model: "gpt-4o-transcribe-diarize", + File: []byte("audio-bytes"), + Filename: "sample.mp3", + TranscriptionParameters: schemas.TranscriptionParameters{ + ExtraParams: map[string]interface{}{ + "chunking_strategy": "auto", + }, + }, + } + + if bifrostErr := ParseTranscriptionFormDataBodyFromRequest(writer, req, schemas.OpenAI); bifrostErr != nil { + t.Fatalf("unexpected bifrost error: %v", bifrostErr.Error.Message) + } + + contentType := writer.FormDataContentType() + if got := multipartFieldValue(t, contentType, body.Bytes(), "chunking_strategy"); got != "auto" { + t.Fatalf("expected chunking_strategy=auto written verbatim, got %q", got) + } + + order := multipartPartOrder(t, contentType, body.Bytes()) + if order[len(order)-1] != "file" { + t.Fatalf("expected file part last, got order %v", order) + } +} + +func TestParseTranscriptionFormDataBodyFromRequest_ChunkingStrategyObject(t *testing.T) { + var body bytes.Buffer + writer := multipart.NewWriter(&body) + req := &OpenAITranscriptionRequest{ + Model: "gpt-4o-transcribe-diarize", + File: []byte("audio-bytes"), + Filename: "sample.mp3", + TranscriptionParameters: schemas.TranscriptionParameters{ + ExtraParams: map[string]interface{}{ + "chunking_strategy": map[string]interface{}{ + "type": "server_vad", + "threshold": 0.5, + }, + }, + }, + } + + if bifrostErr := ParseTranscriptionFormDataBodyFromRequest(writer, req, schemas.OpenAI); bifrostErr != nil { + t.Fatalf("unexpected bifrost error: %v", bifrostErr.Error.Message) + } + + got := multipartFieldValue(t, writer.FormDataContentType(), body.Bytes(), "chunking_strategy") + if got == "" { + t.Fatal("expected chunking_strategy object to be written as a form field") + } + var decoded map[string]interface{} + if err := json.Unmarshal([]byte(got), &decoded); err != nil { + t.Fatalf("expected chunking_strategy to be valid JSON, got %q: %v", got, err) + } + if decoded["type"] != "server_vad" { + t.Fatalf("expected type=server_vad, got %v", decoded["type"]) + } +} diff --git a/transports/bifrost-http/integrations/openai.go b/transports/bifrost-http/integrations/openai.go index 50c038f5a5..68a488be22 100644 --- a/transports/bifrost-http/integrations/openai.go +++ b/transports/bifrost-http/integrations/openai.go @@ -3111,6 +3111,22 @@ func parseTranscriptionMultipartRequest(ctx *fasthttp.RequestCtx, req interface{ transcriptionReq.Stream = &stream } + // chunking_strategy is OpenAI-specific (required by diarization models). It is a + // Union["auto", server_vad object], so decode object-shaped values and pass + // plain strings (e.g. "auto") through verbatim via ExtraParams passthrough. + if csValues := form.Value["chunking_strategy"]; len(csValues) > 0 && csValues[0] != "" { + raw := csValues[0] + if transcriptionReq.ExtraParams == nil { + transcriptionReq.ExtraParams = map[string]interface{}{} + } + var obj map[string]interface{} + if err := json.Unmarshal([]byte(raw), &obj); err == nil { + transcriptionReq.ExtraParams["chunking_strategy"] = obj + } else { + transcriptionReq.ExtraParams["chunking_strategy"] = raw + } + } + return nil }