Skip to content

Commit d3e17a6

Browse files
authored
fix: OpenAI speech handler endpoint & errors (#34)
1 parent 5f391d4 commit d3e17a6

File tree

1 file changed

+35
-21
lines changed

1 file changed

+35
-21
lines changed

pkg/backend/openai/speech.go

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,55 +16,69 @@ import (
1616
)
1717

1818
func HandleSpeech(c echo.Context, options mo.Option[types.SpeechRequestOptions]) mo.Result[any] {
19+
// Extract options safely once
20+
opt := options.MustGet()
21+
1922
values := types.OpenAISpeechRequestOptions{
20-
Model: options.MustGet().Model,
21-
Input: options.MustGet().Input,
22-
Voice: options.MustGet().Voice,
23-
ResponseFormat: options.MustGet().ResponseFormat,
24-
Speed: options.MustGet().Speed,
23+
Model: opt.Model,
24+
Input: opt.Input,
25+
Voice: opt.Voice,
26+
ResponseFormat: opt.ResponseFormat,
27+
Speed: opt.Speed,
2528
}
2629

2730
payload := lo.Must(json.Marshal(values))
2831

2932
req, err := http.NewRequestWithContext(
3033
c.Request().Context(),
3134
http.MethodPost,
32-
"https://openai.com/v1/audio/speech",
35+
"https://api.openai.com/v1/audio/speech",
3336
bytes.NewBuffer(payload),
3437
)
3538
if err != nil {
3639
return mo.Err[any](apierrors.NewErrInternal().WithCaller())
3740
}
3841

39-
// Proxy the Authorization header
4042
req.Header.Set("Authorization", c.Request().Header.Get("Authorization"))
4143
req.Header.Set("Content-Type", "application/json")
4244

4345
res, err := http.DefaultClient.Do(req)
4446
if err != nil {
45-
return mo.Err[any](apierrors.NewErrBadGateway().WithDetail(err.Error()).WithError(err).WithCaller())
47+
return mo.Err[any](
48+
apierrors.NewErrBadGateway().
49+
WithDetail(err.Error()).
50+
WithError(err).
51+
WithCaller(),
52+
)
4653
}
47-
4854
defer func() { _ = res.Body.Close() }()
4955

50-
if res.StatusCode >= 400 && res.StatusCode < 600 {
56+
if res.StatusCode >= 400 {
57+
ct := res.Header.Get("Content-Type")
5158
switch {
52-
case strings.HasPrefix(res.Header.Get("Content-Type"), "application/json"):
53-
return mo.Err[any](apierrors.
54-
NewUpstreamError(res.StatusCode).
55-
WithDetail(utils.NewJSONResponseError(res.StatusCode, res.Body).OrEmpty().Error()))
56-
case strings.HasPrefix(res.Header.Get("Content-Type"), "text/"):
57-
return mo.Err[any](apierrors.
58-
NewUpstreamError(res.StatusCode).
59-
WithDetail(utils.NewTextResponseError(res.StatusCode, res.Body).OrEmpty().Error()))
59+
case strings.HasPrefix(ct, "application/json"):
60+
return mo.Err[any](
61+
apierrors.NewUpstreamError(res.StatusCode).
62+
WithDetail(utils.NewJSONResponseError(res.StatusCode, res.Body).OrEmpty().Error()),
63+
)
64+
case strings.HasPrefix(ct, "text/"):
65+
return mo.Err[any](
66+
apierrors.NewUpstreamError(res.StatusCode).
67+
WithDetail(utils.NewTextResponseError(res.StatusCode, res.Body).OrEmpty().Error()),
68+
)
6069
default:
61-
slog.Warn("unknown upstream error with unknown Content-Type",
70+
slog.Warn("unknown upstream error",
6271
slog.Int("status", res.StatusCode),
63-
slog.String("content_type", res.Header.Get("Content-Type")),
72+
slog.String("content_type", ct),
6473
slog.String("content_length", res.Header.Get("Content-Length")),
6574
)
75+
return mo.Err[any](
76+
apierrors.NewUpstreamError(res.StatusCode).
77+
WithDetail("unknown Content-Type: " + ct),
78+
)
6679
}
6780
}
6881

69-
return mo.Ok[any](c.Stream(http.StatusOK, "audio/mp3", res.Body))
82+
// Stream audio response with correct upstream content type
83+
return mo.Ok[any](c.Stream(http.StatusOK, res.Header.Get("Content-Type"), res.Body))
7084
}

0 commit comments

Comments
 (0)