feat(transcribe): the gateway sends language=sv, omits it for auto, and refuses anything else (#61)
The request used to hard-code language=sv for every upload, so a recording in another language was decoded as Swedish. The language now comes from the context; no language in the context is still sv. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
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
|
var prefix bytes.Buffer
|
||||||
mw := multipart.NewWriter(&prefix)
|
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 {
|
if err := mw.WriteField(kv[0], kv[1]); err != nil {
|
||||||
return domain.Transcription{}, fmt.Errorf("transcribe: build request: %w", err)
|
return domain.Transcription{}, fmt.Errorf("transcribe: build request: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,3 +255,48 @@ func TestGateway_AnErrorBodyIsBoundedToTwoHundredBytesInTheStatusError(t *testin
|
|||||||
require.ErrorAs(t, err, &se)
|
require.ErrorAs(t, err, &se)
|
||||||
assert.LessOrEqual(t, len(se.Snippet), 200, "a gateway's whole body must not become part of an error")
|
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")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user