fix(voice): 修正火山 V3 流式端点鉴权头(真火山联调验通协议格式)

联调发现 Authorization: Bearer 不被 openspeech V3 二进制流端点接受。改用官方规范头:
- X-Api-App-Key: 固定常量 PlgvMymc7f3tQnJ6(所有客户端同值,缺它 400 "app key not found")
- X-Api-Access-Key: 新版 API Key(账号鉴权)
- X-Api-App-ID: 账号 App ID(解析资源授权,缺它 401 "grant not found")
- X-Api-Resource-Id / X-Api-Connect-Id

改动:Config 加 AppID 字段;frame.go setVolcAuthHeaders 统一挂头 + handshakeDetail
榨取握手失败的 HTTP 状态/logid/body(联调可诊断);asr/tts 共用;voicecheck 加握手探针
分别验 ASR/TTS 端点;voiceconfig/voicecheck 读 VOLC_APP_ID。

现状:协议格式已被真火山接受(过了 400 格式错,进到账号/资源授权查询)。剩 App ID +
资源开通确认(账号侧,联调补)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-22 09:53:26 +08:00
parent b718bdbb21
commit bb9c73a9b7
6 changed files with 63 additions and 14 deletions
+41 -1
View File
@@ -1,6 +1,46 @@
package voice
import "encoding/binary"
import (
"encoding/binary"
"fmt"
"io"
"net/http"
"strings"
)
// volcAppKey 是火山语音网关 V3 端点(ASR/TTS/实时对话)**固定**的 App Key 常量——所有客户端
// 都用同一个值(官方 demo 硬编码),与账号无关;真正的账号鉴权走 X-Api-Access-Key(新版 API Key)。
// 缺这个头握手会被拒:HTTP 400 "app key not found in header or query"。
const volcAppKey = "PlgvMymc7f3tQnJ6"
// setVolcAuthHeaders 按火山 V3 二进制流端点的鉴权规范挂头:固定 App Key + 账号 App ID +
// 新版 API Key(Access Key) + 资源 ID + 连接 ID。**不是** Authorization: Bearer(那是 OpenAI
// 兼容端点用的,流式二进制端点不认)。App ID 用于解析账号资源授权(缺它会 401 grant not found)。
func setVolcAuthHeaders(hdr http.Header, apiKey, appID, resourceID string) {
hdr.Set("X-Api-App-Key", volcAppKey)
if appID != "" {
hdr.Set("X-Api-App-ID", appID)
}
hdr.Set("X-Api-Access-Key", apiKey)
hdr.Set("X-Api-Resource-Id", resourceID)
hdr.Set("X-Api-Connect-Id", newConnectID())
}
// handshakeDetail 从失败的 WS 握手响应里榨出可诊断信息:HTTP 状态 + 火山排障用的
// X-Tt-Logid + 响应体(鉴权/权限/路径错都在这里能看出来)。resp 为 nil 时返回空串。
func handshakeDetail(resp *http.Response) string {
if resp == nil {
return ""
}
body := ""
if resp.Body != nil {
b, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
_ = resp.Body.Close()
body = strings.TrimSpace(string(b))
}
logid := resp.Header.Get("X-Tt-Logid")
return fmt.Sprintf(" [HTTP %d logid=%s body=%q]", resp.StatusCode, logid, body)
}
// 火山语音 WebSocket 二进制帧编解码(ASR / TTS 共用)。协议(从官方参考实现核实):
//