* fix(asr): 火山 ApiKey 模式改用官方端点,修复 45000010 (#931) ApiKey 鉴权模式此前使用 wss://openspeech.bytedance.com/api/v3/plan/sauc/bigmodel_async,官方文档(docs/6561/1354869)中不存在 /plan/ 路径,导致服务端鉴权层返回 45000010 AuthenticationError(The API key or AK/SK in the request is missing or invalid)。 - 端点改为与 AppIdToken 模式一致的官方双向流式优化版端点 wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async - 按官方鉴权表补充 X-Api-Request-Id(复用会话 connect_id)与 X-Api-Sequence(固定 -1)头 - 测试断言两模式端点一致并覆盖新增请求头 * docs(asr): 补充新版控制台 API Key 配置说明,澄清 Key 来源 - docs/volcengine-setup.md 新增「新版控制台(API Key 方式)」章节(官方链接 console.volcengine.com/speech/new/setting/apikeys) - 设置页文案不再称「方舟 API Key」,改为「新版控制台 API Key」并在提示中给出官方创建入口(en/ja/ko/zh-CN/zh-TW 同步) * fix(asr): use fresh request IDs for connection retries
This commit is contained in:
1 parent
a92ada4a7e
commit
d961248210
7 files changed
+57
-14
No files matched your search
@@ -33,3 +33,18 @@
|
||||
不用填:
|
||||
|
||||
- `Secret Key`
|
||||
|
||||
## 新版控制台(API Key 方式)
|
||||
|
||||
新版豆包语音控制台统一使用单个 `API Key` 鉴权,无需 `APP ID` / `Access Token`(旧版应用方式见上文)。
|
||||
|
||||
1. 在新版语音控制台创建 API Key
|
||||
<https://console.volcengine.com/speech/new/setting/apikeys>
|
||||
|
||||
2. 在 OpenLess 的 `Settings -> Providers -> ASR` 中:
|
||||
|
||||
- 鉴权模式选择「新版控制台 API Key」
|
||||
- 填入上一步创建的 `API Key`
|
||||
- `Resource ID` 保持默认 `volc.seedasr.sauc.duration`(豆包流式语音识别模型 2.0 · 小时版)
|
||||
|
||||
新旧两种模式共享同一 WebSocket 端点(`wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async`),仅握手鉴权头不同(新版为 `X-Api-Key` 单头)。官方接口文档:<https://www.volcengine.com/docs/6561/1354869>
|
||||
@@ -24,8 +24,11 @@ use uuid::Uuid;
|
||||
use super::frame::{self, Flags, MessageType, Serialization};
|
||||
use super::{AudioConsumer, DictionaryHotword, RawTranscript};
|
||||
|
||||
/// 官方「大模型流式语音识别 API」(双向流式·优化版)端点:
|
||||
/// https://www.volcengine.com/docs/6561/1354869
|
||||
/// 新旧两种鉴权模式共享同一端点,仅握手鉴权头不同。
|
||||
const ENDPOINT_APP_ID_TOKEN: &str = "wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async";
|
||||
const ENDPOINT_API_KEY: &str = "wss://openspeech.bytedance.com/api/v3/plan/sauc/bigmodel_async";
|
||||
const ENDPOINT_API_KEY: &str = "wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async";
|
||||
/// 200 ms of 16 kHz / 16-bit / mono PCM.
|
||||
const TARGET_AUDIO_CHUNK_BYTES: usize = 6_400;
|
||||
/// 16 kHz · 16-bit · mono = 32 000 bytes/sec → 32 bytes/ms.
|
||||
@@ -306,6 +309,7 @@ impl VolcengineStreamingASR {
|
||||
fn build_connect_request(
|
||||
&self,
|
||||
connect_id: &str,
|
||||
request_id: &str,
|
||||
) -> Result<tokio_tungstenite::tungstenite::handshake::client::Request, VolcengineASRError>
|
||||
{
|
||||
let endpoint = match &self.credentials.auth_mode {
|
||||
@@ -352,6 +356,15 @@ impl VolcengineStreamingASR {
|
||||
HeaderValue::from_str(connect_id)
|
||||
.map_err(|e| VolcengineASRError::ConnectionFailed(e.to_string()))?,
|
||||
);
|
||||
// 官方鉴权表(docs/6561/1354869)要求其余两个头:
|
||||
// X-Api-Request-Id(任务 ID,官方推荐随机 UUID;每次握手尝试独立生成)与
|
||||
// X-Api-Sequence(发包序号,固定值 -1)。
|
||||
headers.insert(
|
||||
"X-Api-Request-Id",
|
||||
HeaderValue::from_str(request_id)
|
||||
.map_err(|e| VolcengineASRError::ConnectionFailed(e.to_string()))?,
|
||||
);
|
||||
headers.insert("X-Api-Sequence", HeaderValue::from_static("-1"));
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
@@ -363,7 +376,8 @@ impl VolcengineStreamingASR {
|
||||
let mut attempt = 0usize;
|
||||
loop {
|
||||
attempt += 1;
|
||||
let request = self.build_connect_request(connect_id)?;
|
||||
let request_id = Uuid::new_v4().to_string();
|
||||
let request = self.build_connect_request(connect_id, &request_id)?;
|
||||
match tokio::time::timeout(CONNECT_TIMEOUT, connect_async(request)).await {
|
||||
Ok(Ok((ws, _resp))) => return Ok(ws),
|
||||
Ok(Err(e)) => {
|
||||
@@ -983,7 +997,9 @@ mod tests {
|
||||
},
|
||||
vec![],
|
||||
);
|
||||
let req = asr.build_connect_request("connect-id").unwrap();
|
||||
let req = asr
|
||||
.build_connect_request("connect-id", "request-id")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
req.uri().to_string(),
|
||||
endpoint,
|
||||
@@ -1007,8 +1023,20 @@ mod tests {
|
||||
);
|
||||
// 两种模式都必须携带资源与连接标识头。
|
||||
assert!(headers.contains_key("X-Api-Resource-Id"));
|
||||
assert!(headers.contains_key("X-Api-Connect-Id"));
|
||||
assert_eq!(headers.get("X-Api-Connect-Id").unwrap(), "connect-id");
|
||||
// 官方鉴权表要求的其余头(docs/6561/1354869)。
|
||||
assert_eq!(headers.get("X-Api-Request-Id").unwrap(), "request-id");
|
||||
assert_ne!(
|
||||
headers.get("X-Api-Request-Id"),
|
||||
headers.get("X-Api-Connect-Id"),
|
||||
"任务 ID 不应复用会话连接 ID"
|
||||
);
|
||||
assert_eq!(headers.get("X-Api-Sequence").unwrap(), "-1");
|
||||
}
|
||||
// 回归:新旧两种鉴权模式共享同一官方端点(docs/6561/1354869),
|
||||
// 曾因 ApiKey 模式误用 /api/v3/plan/... 路径导致 45000010 AuthenticationError。
|
||||
assert_eq!(ENDPOINT_API_KEY, ENDPOINT_APP_ID_TOKEN);
|
||||
assert_eq!(ENDPOINT_API_KEY, "wss://openspeech.bytedance.com/api/v3/sauc/bigmodel_async");
|
||||
}
|
||||
|
||||
/// 构造一个握手阶段返回给定 HTTP 状态码的 tungstenite 错误,用于分类测试。
|
||||
|
||||
@@ -989,9 +989,9 @@ export const en: typeof zhCN = {
|
||||
volcengineResourceIdLabel: 'Resource ID',
|
||||
volcengineAuthModeLabel: 'Auth mode',
|
||||
volcengineAuthModeAppIdToken: 'Legacy app (APP ID + Access Token)',
|
||||
volcengineAuthModeApiKey: 'Ark API Key',
|
||||
volcengineAuthModeApiKey: 'API Key (new console)',
|
||||
volcengineMappingNote: 'Secret Key is not required right now. Resource ID defaults to volc.seedasr.sauc.duration.',
|
||||
volcengineApiKeyNote: 'Authenticate with Volcengine Ark speech model API Key, no APP ID needed. Resource ID defaults to volc.seedasr.sauc.duration.',
|
||||
volcengineApiKeyNote: 'Authenticate with an API Key created in the new speech console — no APP ID needed. Create it under API Keys management: console.volcengine.com/speech/new/setting/apikeys. Resource ID defaults to volc.seedasr.sauc.duration.',
|
||||
xfyunAppIdLabel: 'AppID',
|
||||
xfyunApiKeyLabel: 'API Key',
|
||||
xfyunNote: 'Get AppID and API Key from the iFlytek Open Platform "Realtime ASR" service page. Audio is 16 kHz / 16-bit / mono PCM; the standard API has no hotword parameter (configure personalized hotwords in the iFlytek console), and the language defaults to Mandarin Chinese.',
|
||||
|
||||
@@ -991,9 +991,9 @@ export const ja: typeof zhCN = {
|
||||
volcengineResourceIdLabel: 'Resource ID',
|
||||
volcengineAuthModeLabel: '認証モード',
|
||||
volcengineAuthModeAppIdToken: 'レガシーアプリ(APP ID + Access Token)',
|
||||
volcengineAuthModeApiKey: 'Ark API Key',
|
||||
volcengineAuthModeApiKey: '新版コンソール API Key',
|
||||
volcengineMappingNote: 'Secret Key は現在不要。Resource ID のデフォルトは volc.seedasr.sauc.duration。',
|
||||
volcengineApiKeyNote: 'Volcengine Ark 音声モデルの API Key で認証、APP ID は不要です。Resource ID のデフォルトは volc.seedasr.sauc.duration。',
|
||||
volcengineApiKeyNote: '新版スピーチコンソールで作成した API Key で認証します(APP ID 不要)。API Key はスピーチコンソールの「API Key 管理」で作成:console.volcengine.com/speech/new/setting/apikeys。Resource ID のデフォルトは volc.seedasr.sauc.duration。',
|
||||
xfyunAppIdLabel: 'AppID',
|
||||
xfyunApiKeyLabel: 'API Key',
|
||||
xfyunNote: 'iFlytek オープンプラットフォームの「リアルタイム音声認識」サービスページで AppID と API Key を取得します。音声は 16kHz / 16bit / モノラル PCM。標準版 API にホットワード引数はありません(iFlytek コンソールで個別ホットワードを設定)。言語はデフォルトで中国語(普通話)です。',
|
||||
|
||||
@@ -991,9 +991,9 @@ export const ko: typeof zhCN = {
|
||||
volcengineResourceIdLabel: 'Resource ID',
|
||||
volcengineAuthModeLabel: '인증 모드',
|
||||
volcengineAuthModeAppIdToken: '레거시 앱 (APP ID + Access Token)',
|
||||
volcengineAuthModeApiKey: 'Ark API Key',
|
||||
volcengineAuthModeApiKey: '새 콘솔 API Key',
|
||||
volcengineMappingNote: 'Secret Key 는 현재 입력 불필요. Resource ID 기본값은 volc.seedasr.sauc.duration.',
|
||||
volcengineApiKeyNote: 'Volcengine Ark 음성 모델 API 키로 인증하며 APP ID는 필요하지 않습니다. Resource ID 기본값은 volc.seedasr.sauc.duration입니다.',
|
||||
volcengineApiKeyNote: '새 음성 콘솔에서 만든 API Key로 인증하며 APP ID는 필요 없습니다. API Key는 음성 콘솔의 "API Key 관리"에서 생성합니다: console.volcengine.com/speech/new/setting/apikeys. Resource ID 기본값은 volc.seedasr.sauc.duration입니다.',
|
||||
xfyunAppIdLabel: 'AppID',
|
||||
xfyunApiKeyLabel: 'API Key',
|
||||
xfyunNote: 'iFlytek 오픈 플랫폼 "실시간 음성 인식" 서비스 페이지에서 AppID와 API Key를 가져옵니다. 오디오는 16kHz/16bit/모노 PCM입니다. 표준 API에는 핫워드 매개변수가 없으며(iFlytek 콘솔에서 개별 핫워드 설정), 언어는 기본적으로 중국어(보통화)입니다.',
|
||||
|
||||
@@ -987,9 +987,9 @@ export const zhCN = {
|
||||
volcengineResourceIdLabel: 'Resource ID',
|
||||
volcengineAuthModeLabel: '鉴权模式',
|
||||
volcengineAuthModeAppIdToken: '旧版应用(APP ID + Access Token)',
|
||||
volcengineAuthModeApiKey: '方舟 API Key',
|
||||
volcengineAuthModeApiKey: '新版控制台 API Key',
|
||||
volcengineMappingNote: 'Secret Key 当前无需填写。Resource ID 默认使用 volc.seedasr.sauc.duration。',
|
||||
volcengineApiKeyNote: '使用火山方舟语音模型的 API Key 鉴权,无需 APP ID。Resource ID 默认使用 volc.seedasr.sauc.duration。',
|
||||
volcengineApiKeyNote: '使用新版语音控制台创建的 API Key 鉴权,无需 APP ID。API Key 在语音控制台「API Key 管理」中创建:console.volcengine.com/speech/new/setting/apikeys。Resource ID 默认使用 volc.seedasr.sauc.duration。',
|
||||
xfyunAppIdLabel: 'AppID',
|
||||
xfyunApiKeyLabel: 'API Key',
|
||||
xfyunNote: '在讯飞开放平台「实时语音转写」服务页获取 AppID 与 API Key。音频为 16kHz/16bit/单声道 PCM;标准版接口暂不支持热词参数(可在讯飞控制台配置个性化热词),语种默认中文普通话。',
|
||||
|
||||
@@ -989,9 +989,9 @@ export const zhTW: typeof zhCN = {
|
||||
volcengineResourceIdLabel: 'Resource ID',
|
||||
volcengineAuthModeLabel: '鑑權模式',
|
||||
volcengineAuthModeAppIdToken: '舊版應用(APP ID + Access Token)',
|
||||
volcengineAuthModeApiKey: '方舟 API Key',
|
||||
volcengineAuthModeApiKey: '新版控制台 API Key',
|
||||
volcengineMappingNote: 'Secret Key 當前無需填寫。Resource ID 默認使用 volc.seedasr.sauc.duration。',
|
||||
volcengineApiKeyNote: '使用火山方舟語音模型的 API Key 鑑權,無需 APP ID。Resource ID 預設使用 volc.seedasr.sauc.duration。',
|
||||
volcengineApiKeyNote: '使用新版語音控制台建立的 API Key 鑑權,無需 APP ID。API Key 可在語音控制台「API Key 管理」建立:console.volcengine.com/speech/new/setting/apikeys。Resource ID 預設使用 volc.seedasr.sauc.duration。',
|
||||
xfyunAppIdLabel: 'AppID',
|
||||
xfyunApiKeyLabel: 'API Key',
|
||||
xfyunNote: '在訊飛開放平台「即時語音轉寫」服務頁取得 AppID 與 API Key。音訊為 16kHz/16bit/單聲道 PCM;標準版介面暫不支援熱詞參數(可在訊飛控制台設定個人化熱詞),語種預設中文普通話。',
|
||||
|
||||
Reference in new issue
Block a user