feat(uploads): choose the language of a recording; Swedish default, Other = auto (#61 interim) #62

Merged
mathias merged 10 commits from sprint/language into main 2026-10-05 04:59:03 +00:00
2 changed files with 57 additions and 1 deletions
Showing only changes of commit 0b807c6bcd - Show all commits
+12 -1
View File
@@ -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)
}
@@ -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")
}