diff --git a/internal/adapters/transcribe/gateway.go b/internal/adapters/transcribe/gateway.go index 106798a..a73aa22 100644 --- a/internal/adapters/transcribe/gateway.go +++ b/internal/adapters/transcribe/gateway.go @@ -71,9 +71,20 @@ func (g *Gateway) Transcribe(ctx context.Context, path string) (domain.Transcrip return domain.Transcription{}, fmt.Errorf("transcribe: stat audio: %w", err) } + // The language is one request field and a closed set: sv is sent, auto sends nothing so + // the engine detects it, and anything else never leaves this process (tapir#61). + fields := [][2]string{{"model", g.model}, {"response_format", "verbose_json"}} + switch lang := domain.LanguageFrom(ctx); lang { + case domain.LangSwedish: + fields = append(fields, [2]string{"language", string(lang)}) + case domain.LangAuto: + default: + return domain.Transcription{}, fmt.Errorf("transcribe: language %q is not one tapir sends", lang) + } + var prefix bytes.Buffer mw := multipart.NewWriter(&prefix) - for _, kv := range [][2]string{{"model", g.model}, {"response_format", "verbose_json"}, {"language", "sv"}} { + for _, kv := range fields { if err := mw.WriteField(kv[0], kv[1]); err != nil { return domain.Transcription{}, fmt.Errorf("transcribe: build request: %w", err) } diff --git a/internal/adapters/transcribe/gateway_test.go b/internal/adapters/transcribe/gateway_test.go index 97c655e..53ff943 100644 --- a/internal/adapters/transcribe/gateway_test.go +++ b/internal/adapters/transcribe/gateway_test.go @@ -255,3 +255,48 @@ func TestGateway_AnErrorBodyIsBoundedToTwoHundredBytesInTheStatusError(t *testin require.ErrorAs(t, err, &se) assert.LessOrEqual(t, len(se.Snippet), 200, "a gateway's whole body must not become part of an error") } + +// The language travels on the context (tapir#61); the request field is the only place it +// takes effect, so these observe the request the gateway fake received. + +func TestGateway_SendsLanguageSvWhenTheContextCarriesNoneAndForSwedish(t *testing.T) { + for name, ctx := range map[string]context.Context{ + "no language in the context": context.Background(), + "swedish": domain.WithLanguage(context.Background(), domain.LangSwedish), + } { + t.Run(name, func(t *testing.T) { + var got seen + srv := serve(t, 200, verboseJSON, &got) + g := transcribe.NewGateway(srv.URL+"/v1", "k", "iguana/kb-whisper", srv.Client()) + + _, err := g.Transcribe(ctx, wavFile(t, "RIFF-synthetic-pcm")) + + require.NoError(t, err) + assert.Equal(t, "sv", got.fields["language"]) + }) + } +} + +func TestGateway_OmitsTheLanguageFieldForAnAutoContextSoTheEngineDetectsIt(t *testing.T) { + var got seen + srv := serve(t, 200, verboseJSON, &got) + g := transcribe.NewGateway(srv.URL+"/v1", "k", "iguana/kb-whisper", srv.Client()) + + _, err := g.Transcribe(domain.WithLanguage(context.Background(), domain.LangAuto), wavFile(t, "RIFF-synthetic-pcm")) + + require.NoError(t, err) + _, present := got.fields["language"] + assert.False(t, present, "an auto upload must not be forced to Swedish") + assert.Equal(t, "iguana/kb-whisper", got.fields["model"], "the rest of the request is unchanged") +} + +func TestGateway_ALanguageOutsideTheClosedSetIsRefusedBeforeAnythingIsSent(t *testing.T) { + var got seen + srv := serve(t, 200, verboseJSON, &got) + g := transcribe.NewGateway(srv.URL+"/v1", "k", "iguana/kb-whisper", srv.Client()) + + _, err := g.Transcribe(domain.WithLanguage(context.Background(), domain.Language("en")), wavFile(t, "RIFF-synthetic-pcm")) + + require.Error(t, err) + assert.Empty(t, got.method, "no request may reach the engine with a language the closed set does not hold") +}