diff --git a/core/http/endpoints/openai/transcription.go b/core/http/endpoints/openai/transcription.go index e57aac141..920094f84 100644 --- a/core/http/endpoints/openai/transcription.go +++ b/core/http/endpoints/openai/transcription.go @@ -33,6 +33,29 @@ import ( // @Param stream formData boolean false "stream partial results as SSE" // @Success 200 {object} map[string]string "Response" // @Router /v1/audio/transcriptions [post] +// resolveTranscriptionLanguage picks the transcription language. The request +// form field (OpenAI's `language` param) wins, then any language carried on the +// parsed request, and finally the model config default (parameters.language). +func resolveTranscriptionLanguage(formLanguage, requestLanguage, configLanguage string) string { + if formLanguage != "" { + return formLanguage + } + if requestLanguage != "" { + return requestLanguage + } + return configLanguage +} + +// resolveTranscriptionTranslate picks the translate flag: the request form +// field (`translate`, when a valid bool) wins over the model config default +// (parameters.translate). +func resolveTranscriptionTranslate(formTranslate string, configTranslate bool) bool { + if b, err := strconv.ParseBool(formTranslate); err == nil { + return b + } + return configTranslate +} + func TranscriptEndpoint(cl *config.ModelConfigLoader, ml *model.ModelLoader, appConfig *config.ApplicationConfig) echo.HandlerFunc { return func(c echo.Context) error { input, ok := c.Get(middleware.CONTEXT_LOCALS_KEY_LOCALAI_REQUEST).(*schema.OpenAIRequest) @@ -112,10 +135,19 @@ func TranscriptEndpoint(cl *config.ModelConfigLoader, ml *model.ModelLoader, app xlog.Debug("Audio file copied", "dst", dst) + // Language/translate resolve with the request form field taking + // precedence over the model config default (parameters.language / + // parameters.translate). Previously only the parsed request was read, + // which for a multipart upload never carries these, so config-level + // settings were silently ignored and multilingual models (e.g. canary) + // defaulted to English translation. (#10655) + language := resolveTranscriptionLanguage(c.FormValue("language"), input.Language, config.Language) + translate := resolveTranscriptionTranslate(c.FormValue("translate"), config.Translate) + req := backend.TranscriptionRequest{ Audio: dst, - Language: input.Language, - Translate: input.Translate, + Language: language, + Translate: translate, Diarize: diarize, Prompt: prompt, Temperature: temperature, diff --git a/core/http/endpoints/openai/transcription_resolve_test.go b/core/http/endpoints/openai/transcription_resolve_test.go new file mode 100644 index 000000000..18fd403d4 --- /dev/null +++ b/core/http/endpoints/openai/transcription_resolve_test.go @@ -0,0 +1,48 @@ +package openai + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Transcription language/translate resolution", func() { + Describe("resolveTranscriptionLanguage", func() { + It("prefers the request form field over everything", func() { + Expect(resolveTranscriptionLanguage("en", "fr", "ru")).To(Equal("en")) + }) + + It("falls back to the parsed request when the form field is empty", func() { + Expect(resolveTranscriptionLanguage("", "fr", "ru")).To(Equal("fr")) + }) + + It("falls back to the model config default when nothing else is set", func() { + // The reporter set parameters.language: ru in the model YAML but sent + // no language form field; the config value must be honored. (#10655) + Expect(resolveTranscriptionLanguage("", "", "ru")).To(Equal("ru")) + }) + + It("returns empty when no source provides a language", func() { + Expect(resolveTranscriptionLanguage("", "", "")).To(Equal("")) + }) + }) + + Describe("resolveTranscriptionTranslate", func() { + It("honors a request form field of true", func() { + Expect(resolveTranscriptionTranslate("true", false)).To(BeTrue()) + }) + + It("honors a request form field of false over a config default of true", func() { + Expect(resolveTranscriptionTranslate("false", true)).To(BeFalse()) + }) + + It("falls back to the model config default when the form field is absent", func() { + // parameters.translate: true in the YAML must be honored when no form + // field overrides it. + Expect(resolveTranscriptionTranslate("", true)).To(BeTrue()) + }) + + It("defaults to the config value (false) when neither is set", func() { + Expect(resolveTranscriptionTranslate("", false)).To(BeFalse()) + }) + }) +})