新版豆包控制台只签发一个 API Key,以 X-Api-Key 发送,不再需要 App ID。 原先这条路径是靠「asr_app_key 是否为空」隐式推断的,而随包配置里 asr_app_key 带有占位符(<YOUR_OWN_DOUBAO_APP_ID>)且非空,全新安装会被 判定为旧版控制台,带着占位符发送 X-Api-App-Key 导致鉴权失败。 改为显式配置项 voice_input.doubao_auth_mode(api_key | legacy): - voice_providers 新增 NormalizeDoubaoAuthMode()/UsesDoubaoLegacyAuth(), 集中处理模式解析。该项缺失时按 asr_app_key 是否为「可用值」推断,占位符 不计入,因此既有配置升级后行为不变。 - DoubaoAsrClient 构造函数改收显式 legacy_auth,新版模式下忽略配置中残留 的 App ID;连接失败提示按模式区分文案。旧版 App ID + Access Token 路径 完整保留。 - 设置界面新增「豆包鉴权方式」下拉框,新版模式下隐藏 App ID 输入框并把 Token 标签改为「API Key」。 同时把官方推荐用于输入法场景的 bigmodel_nostream 整句流式接口做成设置里的 可选项,与原有「接口地址」文本框双向同步。默认端点仍为 bigmodel_async,避免 改变既有用户的实时上屏行为。 新版文档将 payload_msg.result 描述为列表,而原 ParseResponse 只按对象读取, 接上该端点会安静地返回空转写。新增 ExtractTranscript() 同时接受对象与数组两 种结构,一套解析覆盖两个端点。
102 lines
4.8 KiB
C++
102 lines
4.8 KiB
C++
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include "voice-input/voice_providers.h"
|
|
#include "tests/includes/test_framework.h"
|
|
|
|
TEST_CASE(voice_providers_resolve_openai_compatible_defaults)
|
|
{
|
|
REQUIRE(VoiceInput::IsDoubaoAsrProvider("doubao"));
|
|
REQUIRE(VoiceInput::IsDoubaoAsrProvider("Doubao"));
|
|
REQUIRE(!VoiceInput::IsDoubaoAsrProvider("openai"));
|
|
|
|
REQUIRE_EQ(VoiceInput::DefaultAsrEndpoint("openai"), "https://api.openai.com/v1/audio/transcriptions");
|
|
REQUIRE_EQ(VoiceInput::DefaultAsrModel("openai"), "whisper-1");
|
|
REQUIRE_EQ(VoiceInput::DefaultAsrModel("siliconflow"), "FunAudioLLM/SenseVoiceSmall");
|
|
REQUIRE_EQ(VoiceInput::AsrTokenSlotKey("siliconflow"), "asr_token_siliconflow");
|
|
REQUIRE(VoiceInput::IsPlaceholderToken("<YOUR_OWN_DOUBAO_ACCESS_TOKEN>"));
|
|
{
|
|
VoiceInputConfig config;
|
|
config.asr_provider = "doubao";
|
|
config.asr_token = "sk-should-not-be-used";
|
|
config.asr_tokens["doubao"] = "doubao-access";
|
|
config.asr_tokens["siliconflow"] = "sk-siliconflow";
|
|
REQUIRE_EQ(VoiceInput::ResolveAsrToken(config), "doubao-access");
|
|
config.asr_provider = "siliconflow";
|
|
REQUIRE_EQ(VoiceInput::ResolveAsrToken(config), "sk-siliconflow");
|
|
config.asr_provider = "groq";
|
|
REQUIRE_EQ(VoiceInput::ResolveAsrToken(config), "");
|
|
config.asr_provider = "siliconflow";
|
|
REQUIRE_EQ(VoiceInput::ResolveAsrModel(config), "FunAudioLLM/SenseVoiceSmall");
|
|
config.asr_model = "custom/asr-model";
|
|
REQUIRE_EQ(VoiceInput::ResolveAsrModel(config), "custom/asr-model");
|
|
}
|
|
{
|
|
VoiceInputConfig config;
|
|
config.polish_provider = "deepseek";
|
|
config.polish_token = "sk-should-not-be-used";
|
|
config.polish_tokens["siliconflow"] = "sk-sf-polish";
|
|
config.polish_tokens["deepseek"] = "sk-deepseek";
|
|
REQUIRE_EQ(VoiceInput::ResolvePolishToken(config), "sk-deepseek");
|
|
config.polish_provider = "siliconflow";
|
|
REQUIRE_EQ(VoiceInput::ResolvePolishToken(config), "sk-sf-polish");
|
|
}
|
|
REQUIRE_EQ(VoiceInput::DefaultPolishEndpoint("deepseek"), "https://api.deepseek.com/chat/completions");
|
|
REQUIRE_EQ(VoiceInput::DefaultPolishModel("openai"), "gpt-4o-mini");
|
|
}
|
|
|
|
// The new console issues one API Key sent as X-Api-Key; the legacy console needs App ID +
|
|
// Access Token. Picking the wrong header set fails authentication, so the fallback for configs
|
|
// written before doubao_auth_mode existed has to key off a *usable* App ID, not a placeholder.
|
|
TEST_CASE(voice_providers_select_doubao_auth_mode)
|
|
{
|
|
REQUIRE_EQ(VoiceInput::NormalizeDoubaoAuthMode("api_key", "real-app-id"), "api_key");
|
|
REQUIRE_EQ(VoiceInput::NormalizeDoubaoAuthMode("legacy", ""), "legacy");
|
|
REQUIRE_EQ(VoiceInput::NormalizeDoubaoAuthMode("LEGACY", ""), "legacy");
|
|
|
|
// Absent or unrecognised setting: infer from the App ID.
|
|
REQUIRE_EQ(VoiceInput::NormalizeDoubaoAuthMode("", "real-app-id"), "legacy");
|
|
REQUIRE_EQ(VoiceInput::NormalizeDoubaoAuthMode("", ""), "api_key");
|
|
REQUIRE_EQ(VoiceInput::NormalizeDoubaoAuthMode("nonsense", "real-app-id"), "legacy");
|
|
// The shipped placeholders must not be mistaken for a configured legacy console.
|
|
REQUIRE_EQ(VoiceInput::NormalizeDoubaoAuthMode("", "<YOUR_OWN_DOUBAO_APP_ID>"), "api_key");
|
|
REQUIRE_EQ(VoiceInput::NormalizeDoubaoAuthMode("", "<YOUR_ASR_APP_KEY>"), "api_key");
|
|
|
|
VoiceInputConfig config;
|
|
config.doubao_auth_mode = "api_key";
|
|
config.asr_app_key = "real-app-id";
|
|
REQUIRE(!VoiceInput::UsesDoubaoLegacyAuth(config));
|
|
config.doubao_auth_mode = "legacy";
|
|
REQUIRE(VoiceInput::UsesDoubaoLegacyAuth(config));
|
|
config.doubao_auth_mode.clear();
|
|
REQUIRE(VoiceInput::UsesDoubaoLegacyAuth(config));
|
|
config.asr_app_key.clear();
|
|
REQUIRE(!VoiceInput::UsesDoubaoLegacyAuth(config));
|
|
}
|
|
|
|
TEST_CASE(voice_providers_use_builtin_prompt_until_overridden)
|
|
{
|
|
VoiceInputConfig config;
|
|
config.polish_prompt_id = "faithful";
|
|
const std::string faithful = VoiceInput::ResolvePolishSystemPrompt(config);
|
|
REQUIRE(faithful.find("校对") != std::string::npos);
|
|
REQUIRE(faithful.find("<asr_text>") != std::string::npos);
|
|
|
|
config.polish_prompt = "只用这一句。";
|
|
REQUIRE_EQ(VoiceInput::ResolvePolishSystemPrompt(config), "只用这一句。");
|
|
|
|
REQUIRE_EQ(VoiceInput::WrapAsrUserMessage("明天开会"), "<asr_text>\n明天开会\n</asr_text>");
|
|
}
|
|
|
|
TEST_CASE(voice_providers_empty_custom_falls_back_to_cleanup)
|
|
{
|
|
VoiceInputConfig config;
|
|
config.polish_prompt_id = "custom_2";
|
|
config.polish_prompt.clear();
|
|
config.polish_prompt_custom_2 = "第二个自定义提示词";
|
|
REQUIRE_EQ(VoiceInput::ResolvePolishSystemPrompt(config), "第二个自定义提示词");
|
|
config.polish_prompt_custom_2.clear();
|
|
const std::string prompt = VoiceInput::ResolvePolishSystemPrompt(config);
|
|
REQUIRE(prompt.find("整理助手") != std::string::npos);
|
|
}
|