mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-07 06:52:28 +00:00
* feat(voice): voice personality, picker catalog, and prompt layer foundation - voicePickerCatalog.ts: per-backend voice lists for Gemini and Qwen with resolve helpers (resolveGeminiLiveVoice, resolveQwenRealtimeVoice) - voicePersonality.ts: VoicePersonalityPreferences schema, presets, composed system prompt with identity/character/response-length layers - voicePromptLayers.ts: buildResolvedVoiceSystemPrompt, preset delivery snippets - voiceSystemPromptParam.ts: hub-side base64url decode for ?systemPrompt= - voicePickerPreferences.ts, voicePersonalitySession.ts: browser-side encode, decode, and storage helpers - useVoicePersonality: React hook for preferences persistence via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix(voice): preset delivery included when non-balanced preset selected; restore test typecheck - isDefaultVoicePersonality: add preset check so warm/calm/direct presets trigger the delivery snippet instead of being treated as default - web/tsconfig.json: remove test file exclusion from typecheck (restoring strict coverage of test code); fix resulting type error in mock declaration via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix(voice): include use_speaker_boost in ElevenLabs TTS override payload The checkbox persisted the pref but ttsDiffersFromDefault and buildElevenLabsTtsOverride both omitted it, so the setting was never sent to the agent. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * test(voice): update speaker_boost test to assert it IS included in override The previous test asserted use_speaker_boost was omitted; now it's correctly included in the TTS payload. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix(voice): authorize use_speaker_boost in ElevenLabs override schema Add use_speaker_boost to both the VoiceAgentConfig tts override type and the buildVoiceAgentConfig() platform_settings so the field is accepted by the ElevenLabs agent runtime. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix(voice): propagate full language code through composed prompt, not just zh getDefaultVoiceSystemPrompt and resolveComposedVoiceSystemPrompt were filtering language to zh-only before passing to composeVoiceAgentPrompt. Now append buildVoiceLanguageBlock(language) after composition so French, Spanish, Japanese etc. reach Gemini/Qwen sessions correctly. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix(voice): only append language block when language explicitly set Building language block unconditionally when no language is given caused getDefaultVoiceSystemPrompt() to diverge from VOICE_SYSTEM_PROMPT. Only append the block when a code is explicitly provided. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix(voice): always include language block for Gemini/Qwen in composed prompt When auto-detect is on (language=undefined), the composed prompt sent via hub proxy was losing the language auto-detect instruction because the block was only added when language was explicitly set. Now: ElevenLabs skips the block (has its own language field); Gemini/Qwen always include it — undefined produces the auto-detect block, an explicit code produces the appropriate language instruction. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> --------- Co-authored-by: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -444,9 +444,6 @@ export default {
|
||||
'send.blocked.title': 'Cannot send message',
|
||||
'send.blocked.noConnection': 'Not connected to server',
|
||||
'resume.failed.title': 'Resume failed',
|
||||
'resume.unavailable.noTarget': 'This session cannot be resumed. Start a new session in this directory.',
|
||||
'session.inactive.autoResume': 'Session is inactive. Sending will resume it automatically.',
|
||||
'session.inactive.cannotResume': 'Session is inactive and cannot be resumed from here. Start a new session in this directory.',
|
||||
'toast.ready.title': 'Ready for input',
|
||||
'toast.ready.body': '{agent} is waiting in {session}',
|
||||
'toast.permission.title': 'Permission Request',
|
||||
@@ -496,10 +493,88 @@ export default {
|
||||
'settings.chat.surfaceColor.softYellow': 'Soft yellow',
|
||||
'settings.chat.surfaceColor.custom': 'Custom color',
|
||||
'settings.voice.title': 'Voice Assistant',
|
||||
'settings.voice.sounds.title': 'How It Sounds',
|
||||
'settings.voice.responds.title': 'How It Responds',
|
||||
'settings.voice.connection.title': 'Connection & provider',
|
||||
'settings.voice.behaves.title': 'How it behaves',
|
||||
'settings.voice.persona.title': 'Persona & instructions',
|
||||
'settings.voice.advanced.section.title': 'Advanced',
|
||||
'settings.voice.responseLength.label': 'Response length',
|
||||
'settings.voice.responseLength.brief': 'Brief',
|
||||
'settings.voice.responseLength.balanced': 'Balanced',
|
||||
'settings.voice.responseLength.detailed': 'Detailed',
|
||||
'settings.voice.responseLength.brief.hint': 'Keep answers to 1–2 sentences.',
|
||||
'settings.voice.responseLength.balanced.hint': 'Default — the assistant judges length by context.',
|
||||
'settings.voice.responseLength.detailed.hint': 'Give thorough answers when depth is useful.',
|
||||
'settings.voice.opening.label': 'Opening',
|
||||
'settings.voice.opening.greet': 'Greet me',
|
||||
'settings.voice.opening.brief': 'Brief me',
|
||||
'settings.voice.opening.greet.hint': 'Assistant says hello and waits for you to speak.',
|
||||
'settings.voice.opening.brief.hint': 'Assistant summarises agent activity when you connect.',
|
||||
'settings.voice.group.label': 'Voice settings',
|
||||
'settings.voice.group.hint': 'Language and voice options apply to the selected backend.',
|
||||
'settings.voice.backend': 'Voice backend',
|
||||
'settings.voice.language': 'Voice Language',
|
||||
'settings.voice.autoDetect': 'Auto-detect',
|
||||
'settings.voice.voice': 'Voice',
|
||||
'settings.voice.voiceDefault': 'Default',
|
||||
'settings.voice.advanced.title': 'Advanced voice settings',
|
||||
'settings.voice.advanced.hint': 'Identity, character, fixtures, sliders. Defaults are fine — open only if you want to retune.',
|
||||
'settings.voice.advanced.customizedBadge': 'customized',
|
||||
'settings.voice.fixtures.title': 'Platform rules (read-only)',
|
||||
'settings.voice.fixtures.hint': 'Shipped with the app: tools, routing, TTS rules. Updated when you upgrade HAPI — not editable here.',
|
||||
'settings.voice.identity.title': 'Identity & brand',
|
||||
'settings.voice.identity.hint': 'Who the voice assistant is. HAPI is the app; rename or rebrand the persona here (e.g. overseer). Provider guardrails are always applied separately.',
|
||||
'settings.voice.identity.reset': 'Reset identity',
|
||||
'settings.voice.identity.default': 'Bundled default identity',
|
||||
'settings.voice.identity.customized': 'Custom identity',
|
||||
'settings.voice.character.promptTitle': 'Character & speaking style',
|
||||
'settings.voice.character.promptHint': 'Tone and style. Preset delivery text merges here when character is empty. Composed with platform rules at runtime on all voice backends.',
|
||||
'settings.voice.character.reset': 'Reset character',
|
||||
'settings.voice.character.appendPreset': 'Append preset delivery text',
|
||||
'settings.voice.layers.resetAll': 'Reset identity & character',
|
||||
'settings.voice.layers.composedNote': 'Runtime prompt = platform rules + your identity + character (all backends).',
|
||||
'settings.voice.wireBudget.elevenlabs': 'ElevenLabs sends composed instructions on connect; session history is bootstrapped then streamed.',
|
||||
'settings.voice.wireBudget.gemini': 'Gemini Live uses the same composed instructions via the hub proxy.',
|
||||
'settings.voice.wireBudget.qwen': 'Qwen Realtime uses the same composed instructions in session.update.',
|
||||
'settings.voice.wireBudget.storage': 'Local storage budget (chars):',
|
||||
'settings.voice.wireBudget.elevenlabsLimits': 'Wire caps: ~{msg} B per WebRTC message; composed prompt budget ~{prompt} B.',
|
||||
'settings.voice.wireBudget.promptTruncated': 'Your composed prompt was shortened to fit the ElevenLabs wire budget. Shorten identity/character or we will trim further on connect.',
|
||||
'settings.voice.wireBudget.composedSize': 'Last composed prompt size',
|
||||
'settings.voice.contextNotice': 'Last voice session',
|
||||
'settings.voice.character.title': 'Speaking style preset',
|
||||
'settings.voice.character.presetSlidersHint': 'Presets adjust ElevenLabs TTS sliders and optional delivery wording (character layer).',
|
||||
'settings.voice.character.preset.label': 'Preset',
|
||||
'settings.voice.character.preset.balanced': 'Balanced',
|
||||
'settings.voice.character.preset.balancedHint': 'Warm and focused — good for everyday task work.',
|
||||
'settings.voice.character.preset.warm': 'Warm & expressive',
|
||||
'settings.voice.character.preset.warmHint': 'More animated delivery; ElevenLabs audio tags when available.',
|
||||
'settings.voice.character.preset.calm': 'Calm',
|
||||
'settings.voice.character.preset.calmHint': 'Slow, measured tone for long or late sessions.',
|
||||
'settings.voice.character.preset.direct': 'Direct',
|
||||
'settings.voice.character.preset.directHint': 'Terse and efficient — minimal filler.',
|
||||
'settings.voice.character.preset.custom': 'Custom',
|
||||
'settings.voice.character.preset.customHint': 'You control sliders and the system prompt above.',
|
||||
'settings.voice.tuning.title': 'Acoustic tuning',
|
||||
'settings.voice.tuning.elevenlabs': 'ElevenLabs',
|
||||
'settings.voice.tuning.gemini': 'Gemini Live',
|
||||
'settings.voice.tuning.stability': 'Stability',
|
||||
'settings.voice.tuning.stabilityHint': 'Lower = more dynamic; higher = steadier.',
|
||||
'settings.voice.tuning.expressiveness': 'Expressiveness',
|
||||
'settings.voice.tuning.expressivenessHint': 'Style exaggeration (ElevenLabs style).',
|
||||
'settings.voice.tuning.speakingRate': 'Speaking rate',
|
||||
'settings.voice.tuning.speakingRateHint': '0.7–1.2× (ElevenLabs API).',
|
||||
'settings.voice.tuning.similarity': 'Similarity boost',
|
||||
'settings.voice.tuning.similarityHint': 'Closer match to the trained voice.',
|
||||
'settings.voice.tuning.speakerBoost': 'Speaker boost',
|
||||
'settings.voice.tuning.affectiveDialog': 'Affective dialog',
|
||||
'settings.voice.tuning.affectiveDialogHint': 'Adapt delivery to your tone (Gemini v1alpha).',
|
||||
'settings.voice.tuning.qwenHint': 'Qwen has no TTS sliders here — edit the voice system prompt above; emotion adapts from your speech.',
|
||||
'settings.voice.tuning.selectBackend': 'Per-backend sliders appear when a voice backend is selected.',
|
||||
'settings.voice.preview.elevenlabsOnly': 'Voice preview is only available for ElevenLabs',
|
||||
'settings.voice.preview.unavailable': 'Preview unavailable without an ElevenLabs API key',
|
||||
'settings.voice.staticCatalogHint': 'Open the list to see voice character notes. Audio preview is ElevenLabs only.',
|
||||
'settings.voice.session.label': 'Session behavior',
|
||||
'settings.voice.proactive': 'Start voice session with summary',
|
||||
'settings.voice.proactive.description': 'When on, starting a voice session opens with a spoken summary of current agent activity. When off, the assistant greets you and waits for you to speak.',
|
||||
'settings.about.title': 'About',
|
||||
|
||||
@@ -446,9 +446,6 @@ export default {
|
||||
'send.blocked.title': '无法发送消息',
|
||||
'send.blocked.noConnection': '未连接到服务器',
|
||||
'resume.failed.title': '恢复会话失败',
|
||||
'resume.unavailable.noTarget': '无法恢复此会话。请在该目录下新建会话。',
|
||||
'session.inactive.autoResume': '会话已停用。发送消息将自动恢复会话。',
|
||||
'session.inactive.cannotResume': '会话已停用且无法在此恢复。请在该目录下新建会话。',
|
||||
'toast.ready.title': '等待输入',
|
||||
'toast.ready.body': '{agent} 正在 {session} 等待你的输入',
|
||||
'toast.permission.title': '权限请求',
|
||||
@@ -498,10 +495,88 @@ export default {
|
||||
'settings.chat.surfaceColor.softYellow': '柔和黄',
|
||||
'settings.chat.surfaceColor.custom': '自定义颜色',
|
||||
'settings.voice.title': '语音助手',
|
||||
'settings.voice.sounds.title': '声音效果',
|
||||
'settings.voice.responds.title': '对话风格',
|
||||
'settings.voice.connection.title': '连接与提供商',
|
||||
'settings.voice.behaves.title': '对话行为',
|
||||
'settings.voice.persona.title': '角色与指令',
|
||||
'settings.voice.advanced.section.title': '高级',
|
||||
'settings.voice.responseLength.label': '回复长度',
|
||||
'settings.voice.responseLength.brief': '简短',
|
||||
'settings.voice.responseLength.balanced': '适中',
|
||||
'settings.voice.responseLength.detailed': '详细',
|
||||
'settings.voice.responseLength.brief.hint': '回复控制在1-2句话以内。',
|
||||
'settings.voice.responseLength.balanced.hint': '默认——助手根据情境判断回复长度。',
|
||||
'settings.voice.responseLength.detailed.hint': '需要时给出详细完整的回答。',
|
||||
'settings.voice.opening.label': '开场方式',
|
||||
'settings.voice.opening.greet': '打个招呼',
|
||||
'settings.voice.opening.brief': '简报状态',
|
||||
'settings.voice.opening.greet.hint': '助手先问候您,然后等待您开口。',
|
||||
'settings.voice.opening.brief.hint': '连接时助手汇报当前代理活动。',
|
||||
'settings.voice.group.label': '语音设置',
|
||||
'settings.voice.group.hint': '语言和声音选项取决于所选后端。',
|
||||
'settings.voice.backend': '语音后端',
|
||||
'settings.voice.language': '语音语言',
|
||||
'settings.voice.autoDetect': '自动检测',
|
||||
'settings.voice.voice': '声音',
|
||||
'settings.voice.voiceDefault': '默认',
|
||||
'settings.voice.advanced.title': '高级语音设置',
|
||||
'settings.voice.advanced.hint': '身份、性格、平台规则、滑块。默认即可,需调音时再展开。',
|
||||
'settings.voice.advanced.customizedBadge': '已自定义',
|
||||
'settings.voice.fixtures.title': '平台规则(只读)',
|
||||
'settings.voice.fixtures.hint': '随应用发布:工具、路由、语音输出规则。升级 HAPI 时更新,此处不可编辑。',
|
||||
'settings.voice.identity.title': '身份与品牌',
|
||||
'settings.voice.identity.hint': '语音助手是谁。HAPI 是应用本身;可在此改名或换人设。提供商护栏会单独附加。',
|
||||
'settings.voice.identity.reset': '恢复默认身份',
|
||||
'settings.voice.identity.default': '内置默认身份',
|
||||
'settings.voice.identity.customized': '已自定义身份',
|
||||
'settings.voice.character.promptTitle': '性格与表达',
|
||||
'settings.voice.character.promptHint': '语气与风格。性格为空时会合并预设表达文案。运行时与平台规则一起组成完整提示(所有语音后端)。',
|
||||
'settings.voice.character.reset': '恢复默认性格',
|
||||
'settings.voice.character.appendPreset': '追加预设表达文案',
|
||||
'settings.voice.layers.resetAll': '恢复身份与性格',
|
||||
'settings.voice.layers.composedNote': '运行时提示 = 平台规则 + 身份 + 性格(所有后端)。',
|
||||
'settings.voice.wireBudget.elevenlabs': 'ElevenLabs 在连接时发送合成指令;会话历史先握手再流式推送。',
|
||||
'settings.voice.wireBudget.gemini': 'Gemini Live 通过 hub 代理使用相同合成指令。',
|
||||
'settings.voice.wireBudget.qwen': 'Qwen Realtime 在 session.update 中使用相同合成指令。',
|
||||
'settings.voice.wireBudget.storage': '本地存储上限(字符):',
|
||||
'settings.voice.wireBudget.elevenlabsLimits': '传输上限:每条 WebRTC 消息约 {msg} B;合成提示约 {prompt} B。',
|
||||
'settings.voice.wireBudget.promptTruncated': '合成提示已缩短以符合 ElevenLabs 传输上限。请精简身份/性格,否则连接时可能再次截断。',
|
||||
'settings.voice.wireBudget.composedSize': '上次合成提示大小',
|
||||
'settings.voice.contextNotice': '上次语音会话',
|
||||
'settings.voice.character.title': '说话风格预设',
|
||||
'settings.voice.character.presetSlidersHint': '预设调整 ElevenLabs TTS 滑块,并可选择合并到性格层。',
|
||||
'settings.voice.character.preset.label': '预设',
|
||||
'settings.voice.character.preset.balanced': '均衡',
|
||||
'settings.voice.character.preset.balancedHint': '温暖专注,适合日常任务。',
|
||||
'settings.voice.character.preset.warm': '温暖活泼',
|
||||
'settings.voice.character.preset.warmHint': '更有表现力;ElevenLabs 可用音频标签。',
|
||||
'settings.voice.character.preset.calm': '沉稳',
|
||||
'settings.voice.character.preset.calmHint': '慢而稳,适合长会话或夜间。',
|
||||
'settings.voice.character.preset.direct': '干脆',
|
||||
'settings.voice.character.preset.directHint': '简短高效,少寒暄。',
|
||||
'settings.voice.character.preset.custom': '自定义',
|
||||
'settings.voice.character.preset.customHint': '滑块与上方系统提示由你控制。',
|
||||
'settings.voice.tuning.title': '音色与节奏',
|
||||
'settings.voice.tuning.elevenlabs': 'ElevenLabs',
|
||||
'settings.voice.tuning.gemini': 'Gemini Live',
|
||||
'settings.voice.tuning.stability': '稳定性',
|
||||
'settings.voice.tuning.stabilityHint': '越低越有变化;越高越平稳。',
|
||||
'settings.voice.tuning.expressiveness': '表现力',
|
||||
'settings.voice.tuning.expressivenessHint': '风格夸张度(ElevenLabs style)。',
|
||||
'settings.voice.tuning.speakingRate': '语速',
|
||||
'settings.voice.tuning.speakingRateHint': '0.7–1.2×(ElevenLabs API)。',
|
||||
'settings.voice.tuning.similarity': '相似度增强',
|
||||
'settings.voice.tuning.similarityHint': '更接近训练音色。',
|
||||
'settings.voice.tuning.speakerBoost': '说话人增强',
|
||||
'settings.voice.tuning.affectiveDialog': '情感对话',
|
||||
'settings.voice.tuning.affectiveDialogHint': '根据你的语气调整回应(Gemini v1alpha)。',
|
||||
'settings.voice.tuning.qwenHint': 'Qwen 无 TTS 滑块 — 请用角色预设与备注;情绪会从你的语音自适应。',
|
||||
'settings.voice.tuning.selectBackend': '选择语音后端后显示对应滑块。',
|
||||
'settings.voice.preview.elevenlabsOnly': '仅 ElevenLabs 支持试听',
|
||||
'settings.voice.preview.unavailable': '未配置 ElevenLabs API 密钥时无法试听',
|
||||
'settings.voice.staticCatalogHint': '展开列表可查看声音特点说明;仅 ElevenLabs 支持试听。',
|
||||
'settings.voice.session.label': '会话行为',
|
||||
'settings.voice.proactive': '以摘要开始语音会话',
|
||||
'settings.voice.proactive.description': '开启后,启动语音会话时将朗读当前代理活动的摘要。关闭后,助手向您打招呼并等待您先开口。',
|
||||
'settings.about.title': '关于',
|
||||
|
||||
Reference in New Issue
Block a user