现象
在「API → 实时录音」里选任意流式转写服务(OpenAI Realtime · 语音转写 / 语音翻译、阿里云百炼 Paraformer),填好地址和密钥后点「连通性测试」,一定失败:
测试失败:转写服务地址协议不受支持;请使用 HTTPS,本地、私网或 Tailscale 等私有网络服务可使用 HTTP
关键点是这句报错的标签是 转写服务地址,不是 实时转写服务地址 —— 说明它根本没走 WebSocket 客户端。
录音本身不受影响,能正常出实时字幕;坏掉的只有这个自检按钮。但对刚配好服务的人来说,这个按钮是唯一的验证手段,报出来的又是一句「你地址填错了」,很容易让人以为是自己配错、或者服务不可用,然后一直改地址。
复现
- 转写服务选「OpenAI Realtime · 语音转写」
- 服务地址填任何合法的
wss://…,密钥、模型名随便填
- 点「连通性测试」
原因
runAsrConnectivityTest() 无条件走 HTTP 切片上传那条路,没有流式分支:
所以只要 profile.transcribeMode === "streaming"(三个服务全是),端点必然是 wss://,这个按钮就必然失败。
有意思的是 src/main.ts 里那条兜底分支的注释已经写明了这件事:
流式服务但客户端连接失败:保留音频但不做 HTTP 切片转写(端点是 wss://,HTTP 必失败)
—— 录音路径知道 HTTP 走不通,连通性测试没跟上。
顺带:转写客户端不会自动补 ?model=
OpenAIRealtimeTranslationClient.connect() 会自己拼 ?model=:
const sep = this.endpointBase.indexOf("?") >= 0 ? "&" : "?";
const url = this.endpointBase + sep + "model=" + encodeURIComponent(this.model);
但 OpenAIRealtimeTranscriptionClient.connect() 是 new WSCtor(this.endpoint, …),地址原样使用。官方端点没问题(GA 走 session.update 里的 session.type=transcription),但自建 / 中转的 OpenAI 兼容实时服务很多要求 URL 上带 ?model=,不带直接 403、握手都不成立。而设置项的说明只有一句「保持默认即可」,占位符也是不带参数的 wss://api.openai.com/v1/realtime,第三方服务的用户很难猜到要自己拼。
我在一个自建的 OpenAI 兼容 ASR 服务上实测:
| 地址 |
结果 |
wss://<host>/v1 |
HTTP 403 |
wss://<host>/v1/realtime |
HTTP 403 |
wss://<host>/v1/realtime?model=<model> |
101 → transcription_session.created → 正常出字 |
顺带确认协议本身是通的:该服务原样接受了 OpenAIRealtimeTranscriptionClient 发的那份 GA 形态 session.update(session.type=transcription + audio.input.format={type:"audio/pcm",rate:24000}),并回 conversation.item.input_audio_transcription.delta / .completed。所以这里纯粹是地址拼不出来的问题,不是协议不兼容。
期望
- 连通性测试对流式服务改用流式客户端自检,而不是把
wss:// 端点喂给 HTTP 上传
- 端点说明里写清楚第三方服务可能要带
?model=
已提 PR。
环境
- LexVoice 2.1.2
- Obsidian 桌面端 (macOS)
现象
在「API → 实时录音」里选任意流式转写服务(OpenAI Realtime · 语音转写 / 语音翻译、阿里云百炼 Paraformer),填好地址和密钥后点「连通性测试」,一定失败:
关键点是这句报错的标签是
转写服务地址,不是实时转写服务地址—— 说明它根本没走 WebSocket 客户端。录音本身不受影响,能正常出实时字幕;坏掉的只有这个自检按钮。但对刚配好服务的人来说,这个按钮是唯一的验证手段,报出来的又是一句「你地址填错了」,很容易让人以为是自己配错、或者服务不可用,然后一直改地址。
复现
wss://…,密钥、模型名随便填原因
runAsrConnectivityTest()无条件走 HTTP 切片上传那条路,没有流式分支:src/ui/settings-tab.tsrunAsrConnectivityTest()→ 造一段 1 秒静音 webm →transcribeAudio()src/asr/transcribe.tstranscribeAudio()→assertSafeServiceEndpoint(p.endpoint, "http", "转写服务地址")src/shared/util-llm-endpoint.tsgetServiceEndpointSecurityIssue():wss:既不等于https:也不等于http:,落到最后一个return所以只要
profile.transcribeMode === "streaming"(三个服务全是),端点必然是wss://,这个按钮就必然失败。有意思的是
src/main.ts里那条兜底分支的注释已经写明了这件事:—— 录音路径知道 HTTP 走不通,连通性测试没跟上。
顺带:转写客户端不会自动补
?model=OpenAIRealtimeTranslationClient.connect()会自己拼?model=:但
OpenAIRealtimeTranscriptionClient.connect()是new WSCtor(this.endpoint, …),地址原样使用。官方端点没问题(GA 走session.update里的session.type=transcription),但自建 / 中转的 OpenAI 兼容实时服务很多要求 URL 上带?model=,不带直接 403、握手都不成立。而设置项的说明只有一句「保持默认即可」,占位符也是不带参数的wss://api.openai.com/v1/realtime,第三方服务的用户很难猜到要自己拼。我在一个自建的 OpenAI 兼容 ASR 服务上实测:
wss://<host>/v1wss://<host>/v1/realtimewss://<host>/v1/realtime?model=<model>transcription_session.created→ 正常出字顺带确认协议本身是通的:该服务原样接受了
OpenAIRealtimeTranscriptionClient发的那份 GA 形态session.update(session.type=transcription+audio.input.format={type:"audio/pcm",rate:24000}),并回conversation.item.input_audio_transcription.delta/.completed。所以这里纯粹是地址拼不出来的问题,不是协议不兼容。期望
wss://端点喂给 HTTP 上传?model=已提 PR。
环境