feat(provider): 直连 HTTP 与 WebSocket 音频路径返回 *sdk.APIError - #65
Merged
Merged
Conversation
HoneyBBQ
added this pull request to stack #67
September 28, 2026 13:17
HoneyBBQ
force-pushed
the
feat/errors-direct-http
branch
from
September 28, 2026 19:55
1080603 to
66a185b
Compare
HoneyBBQ
force-pushed
the
feat/errors-direct-http
branch
3 times, most recently
from
September 29, 2026 11:45
26ce7d2 to
2022758
Compare
HoneyBBQ
force-pushed
the
feat/errors-direct-http
branch
4 times, most recently
from
September 29, 2026 21:58
8c82ba6 to
a1fa752
Compare
HoneyBBQ
force-pushed
the
feat/errors-direct-http
branch
from
September 29, 2026 22:11
a1fa752 to
b424aba
Compare
…udio paths Speech, transcription and image multipart requests that use http.Client directly now return utils.NewHTTPError on a non-2xx response instead of a formatted string that embedded the response body. Each response is decoded by the decoder for its provider's documented error format and mapped to an ErrorKind. OpenAI and MiMo use errorformat.DecodeOpenAI, Google transcription errorformat.DecodeGoogle, OpenRouter errorformat.DecodeOpenRouter. The Deepgram and ElevenLabs formats, each received by a speech and a transcription package, are added to internal/errorformat. Failures that providers report inside a 200 body (MiniMax base_resp, SAMI status_code, SAMI GetToken) and WebSocket failure frames (DashScope task-failed) become an *sdk.APIError with StatusCode 0. WebSocket handshake rejections with an HTTP response go through NewHTTPError. SpeechStreamResult gains Err(), which reports the error that ended a stream; Bytes returns it. The Volcengine SAMI invoke URL carries the token and app key in its query. A transport failure no longer prints that URL. Network, context and decode errors are still wrapped with %w and are not APIErrors.
…rs they send on errors ElevenLabs error responses carry x-trace-id, equal to detail.request_id; the request-id header appears only on successful responses. Some ElevenLabs endpoints still send only the legacy detail.status field, which now fills Code. MiniMax error bodies often omit trace_id while the Trace-Id header carries the same value.
HoneyBBQ
force-pushed
the
feat/errors-direct-http
branch
from
September 30, 2026 06:53
b424aba to
d267e0c
Compare
HoneyBBQ
marked this pull request as ready for review
September 30, 2026 06:54
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
语音、转录和 multipart 图片请求直接调用
http.Client,不经过FetchJSON。遇到非 2xx 响应时,它们返回fmt.Errorf,错误文本里带着响应体。调用方拿不到*sdk.APIError,也拿不到Kind,而响应体可能把终端用户的输入回显进日志。在 200 响应体或 WebSocket 帧里报告的失败同样是纯字符串。火山引擎 SAMI 的 invoke URL 在 query 里携带 token 和 app key。请求在传输层失败时,
*url.Error会把这个 URL 打印进错误文本。改动
http.Client的路径遇到非 2xx 响应时返回utils.NewHTTPError。每个响应按 provider 文档中的错误格式解码,并映射到ErrorKind。internal/errorformat。OpenRouter 的语音和转录改用共享的 OpenRouter 解码器。base_resp、SAMI 的status_code、SAMI GetToken)和 DashScope 的task-failedWebSocket 帧,返回StatusCode为 0 的*sdk.APIError。WebSocket 握手被拒且带 HTTP 响应时,走NewHTTPError。SpeechStreamResult.Err(),返回导致流结束的错误。Bytes也返回这个错误。{"detail":{"status":"...","message":"..."}},此时Code取status。RequestID取detail.request_id,没有时取x-trace-id响应头,两者是同一个值。trace_id。RequestID先取trace_id,没有时取Trace-Id响应头,两者的值相同。%w包装,不是APIError。验证
go build ./...、go vet ./...、go test ./... -short -count=1 -race和golangci-lint run ./...均通过,go mod tidy无 diff。httptest用例,断言errors.As、StatusCode、Kind,以及Error()不含 key 和 body。authentication。其中包括 DashScope WebSocket 握手的 401,以及 MiniMax 在 200 响应体里返回的 1004。unknown。Deepgram 转录是例外,它对不存在的模型返回 403INSUFFICIENT_PERMISSIONS,Kind 为permission_denied。