-
Notifications
You must be signed in to change notification settings - Fork 625
feat: support voice.ai #1291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support voice.ai #1291
Conversation
📝 WalkthroughWalkthroughAdds Voice.ai Text-to-Speech support: a new VoiceAIProvider with streaming TTS, UI for provider configuration and voice calls, audio message handling and player, provider registration, i18n entries, and build/test config updates for custom Vue elements. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as ChatInput / VoiceCallWidget
participant Provider as VoiceAIProvider
participant API as Voice.ai API
participant Store as Chat Store / Renderer
UI->>Provider: request speech (coreStream / generateSpeech)
Provider->>Provider: extract user text, build TTS request
Provider->>API: POST /api/tts (headers, body, proxy if set)
API-->>Provider: audio response (base64 / data / URL)
Provider->>Store: emit streaming event (imageData / audio)
Store->>Store: create AssistantMessageBlock type:'audio'
Store->>UI: render audio block
UI->>UI: MessageBlockAudio plays audio
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@src/main/presenter/llmProviderPresenter/providers/voiceAIProvider.ts`:
- Around line 91-141: The methods completions, summaries, and generateText call
generateSpeech() but discard its {audioBase64, mimeType} result and only return
an LLMResponse with text; remove the unnecessary generateSpeech() calls to avoid
wasteful TTS API usage. Update the implementations of completions, summaries,
and generateText in voiceAIProvider.ts to only validate input, skip calling
generateSpeech(), and return the appropriate text (e.g., text,
buildShortTitle(text), or prompt) as before; keep generateSpeech() and
coreStream() for the streaming/audio paths if needed.
In `@src/renderer/src/components/message/MessageBlockAudio.vue`:
- Line 64: audioError is initialized but never updated, so the template error
message never appears; wire the component's audio element error handling to set
audioError.value = true on failure and clear it on successful load. Add an error
event listener on the audio DOM ref (or set `@error` on the <audio> element) to
set audioError.value = true, set audioError.value = false in the audio load/play
success handlers (e.g., canplay, play or your existing load/play functions), and
remove listeners in onBeforeUnmount; locate the const audioError = ref(false)
and the audio element/ref handlers to implement these changes.
In `@src/renderer/src/i18n/en-US/settings.json`:
- Around line 496-527: Add the missing "voiceai" translations (same keys as in
en-US: voiceai.title, voiceai.description, voiceai.audioFormat.label,
voiceai.audioFormat.placeholder, voiceai.audioFormat.helper,
voiceai.language.label, voiceai.language.placeholder, voiceai.language.helper,
voiceai.model.label, voiceai.model.placeholder, voiceai.model.helper,
voiceai.agentId.label, voiceai.agentId.placeholder, voiceai.agentId.helper,
voiceai.temperature.label, voiceai.temperature.helper, voiceai.topP.label,
voiceai.topP.helper) into each of the remaining locale JSON files (da-DK, fa-IR,
fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-HK, zh-TW) using the en-US values
as the baseline, preserving exact key names and JSON structure so all 12 locales
contain an identical "voiceai" section.
In `@src/renderer/src/i18n/zh-CN/chat.json`:
- Around line 101-111: The new translation keys audio.play, audio.pause and
call.title, call.description, call.start, call.stop, call.end were added to
zh-CN/chat.json but are missing from the other locale chat files; add those
exact keys (with appropriate translated strings) to each of the remaining locale
files: da-DK, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-HK and zh-TW
so all 12 locales have the same key structure under the "audio" and "call"
objects (preserve key names and JSON structure).
In `@src/renderer/src/i18n/zh-CN/settings.json`:
- Around line 397-428: Add the missing provider.voiceai translation block (same
key names as the existing "voiceai" object: title, description,
audioFormat.{label,placeholder,helper}, language.{label,placeholder,helper},
model.{label,placeholder,helper}, agentId.{label,placeholder,helper},
temperature.{label,helper}, topP.{label,helper}) to each of the 10 locale files:
da-DK, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-HK, zh-TW, keeping
the exact structure used in the current zh-CN snippet and providing appropriate
localized strings for each field (or accurate fallbacks if a translation is
unavailable). Ensure keys are placed under the same parent namespace
(provider.voiceai or top-level "voiceai" depending on project convention), match
spelling/casing of existing keys (e.g., "audioFormat", "agentId"), and run the
linter/locale validation to confirm no missing or extra keys.
🧹 Nitpick comments (9)
electron.vite.config.ts (1)
10-11: Consider extractingisCustomElementto a shared location.The
isCustomElementhelper is duplicated invitest.config.renderer.ts. If more custom elements are added later, both files need updates.♻️ Optional: Extract to shared config
Create a shared file (e.g.,
config/vue-custom-elements.ts):export const isCustomElement = (tag: string) => tag === 'voice-agent-widget' || tag.startsWith('ui-resource-renderer')Then import in both config files.
src/renderer/src/components/message/MessageItemAssistant.vue (1)
173-187: Potential false positive in audio extension detection.The check
lower.includes(ext)on line 184 may produce false positives. For example, a URL likehttps://example.com/mp3player/image.jpgwould incorrectly match.mp3because the string contains "mp3".Consider using a more precise check that verifies the extension appears at the end of the URL path:
♻️ Proposed fix for more precise extension matching
const isAudioBlock = (block: AssistantMessageBlock): boolean => { if (block.type === 'audio') return true if (block.type !== 'image') return false const mimeType = block.image_data?.mimeType?.toLowerCase() || '' if (mimeType.startsWith('audio/')) return true const data = block.image_data?.data || '' if (data.startsWith('data:audio/')) return true if (data.startsWith('imgcache://') || data.startsWith('http://') || data.startsWith('https://')) { const lower = data.toLowerCase() - return AUDIO_EXTENSIONS.some((ext) => lower.includes(ext)) + // Extract path and check if it ends with an audio extension + try { + const url = new URL(lower.replace('imgcache://', 'https://')) + const pathname = url.pathname + return AUDIO_EXTENSIONS.some((ext) => pathname.endsWith(ext)) + } catch { + return AUDIO_EXTENSIONS.some((ext) => lower.endsWith(ext)) + } } return false }src/renderer/settings/components/VoiceAIProviderConfig.vue (2)
158-160: Define TypeScript interface for component props.The props are correctly typed with a TypeScript interface inline. However, consider defining the interface explicitly as per coding guidelines.
♻️ Suggested improvement
+interface Props { + provider: LLM_PROVIDER +} + -defineProps<{ - provider: LLM_PROVIDER -}>() +defineProps<Props>()
216-248: Consider consolidating individual watchers.While the current approach with individual watchers is clear, you could use a single watcher with an object to reduce repetition. This is optional since the current implementation is readable and maintainable.
src/renderer/src/components/chat-input/VoiceCallWidget.vue (2)
79-88: Consider removing unused refs.The
callWidgetPulseref (line 85) andvoiceWidgetReadyref (line 83) are updated but never read in the template or logic. Similarly,voiceWidgetLoading(line 84) andvoiceWidget(line 82) are declared but not used meaningfully.♻️ Potential cleanup
If these refs are intended for future use, consider adding a TODO comment. Otherwise, remove them to reduce dead code:
-const voiceWidget = ref<HTMLElement | null>(null) -const voiceWidgetReady = ref(false) -const voiceWidgetLoading = ref(false) -const callWidgetPulse = ref(false) -let callWidgetPulseTimer: ReturnType<typeof setTimeout> | null = nullThe
voiceWidgetScriptPromiseis correctly used for deduplicating script load requests.
205-209: Timer cleanup on unmount is correct but incomplete.The
callWidgetPulseTimercleanup is good, butcallWidgetPulseTimeris never actually set anywhere in the code. If this timer was removed, the cleanup can also be removed.src/renderer/src/components/message/MessageBlockAudio.vue (1)
44-51: Unusual i18n fallback pattern - consider alignment with project conventions.The try/catch wrapper around
useI18n()is atypical. If this component is intended to work outside the i18n context, consider using a more explicit approach or ensure the component is always rendered within a proper i18n provider.♻️ Alternative approach
If fallback is intentional for edge cases:
import { inject } from 'vue' // Use inject to check for i18n availability more gracefully const i18n = inject('i18n', null) const t = i18n ? useI18n().t : (key: string) => keyMap[key] || keyOr simply ensure this component is always used within a proper Vue app context with i18n configured.
src/main/presenter/llmProviderPresenter/providers/voiceAIProvider.ts (2)
185-197: Consider clarifyingcontextLengthandmaxTokensfor TTS models.These fields (
contextLength: 4096,maxTokens: 2048) are likely required by theMODEL_METAinterface but are not meaningful for TTS providers. Consider adding a comment explaining these are placeholder values to satisfy the interface, or using more appropriate values (e.g., character limits for TTS input).
161-164: Consider renaming the event to reflect its broader media purpose, or create a dedicatedaudioDataevent.The code uses
createStreamEvent.imageData()to emit audio data. While functionally correct due to generic field names (data,mimeType), the naming suggests the event is exclusively for images. Either renameImageDataStreamEventtoMediaDataStreamEventorBinaryDataStreamEventto clarify it handles multiple media types, or add a dedicatedaudioDataevent type to the event interface for semantic clarity.
| public async completions( | ||
| messages: ChatMessage[], | ||
| modelId: string, | ||
| temperature?: number, | ||
| _maxTokens?: number | ||
| ): Promise<LLMResponse> { | ||
| const text = this.extractLatestUserText(messages) | ||
| if (!text) { | ||
| throw new Error('No user text provided for Voice.ai TTS') | ||
| } | ||
|
|
||
| await this.generateSpeech(text, modelId, temperature) | ||
|
|
||
| return { | ||
| content: text | ||
| } | ||
| } | ||
|
|
||
| public async summaries( | ||
| text: string, | ||
| modelId: string, | ||
| temperature?: number, | ||
| _maxTokens?: number | ||
| ): Promise<LLMResponse> { | ||
| if (!text) { | ||
| throw new Error('No text provided for Voice.ai TTS') | ||
| } | ||
|
|
||
| await this.generateSpeech(text, modelId, temperature) | ||
|
|
||
| return { | ||
| content: this.buildShortTitle(text) | ||
| } | ||
| } | ||
|
|
||
| public async generateText( | ||
| prompt: string, | ||
| modelId: string, | ||
| temperature?: number, | ||
| _maxTokens?: number | ||
| ): Promise<LLMResponse> { | ||
| if (!prompt) { | ||
| throw new Error('No prompt provided for Voice.ai TTS') | ||
| } | ||
|
|
||
| await this.generateSpeech(prompt, modelId, temperature) | ||
|
|
||
| return { | ||
| content: prompt | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for LLMResponse type definition
rg "type\s+LLMResponse\s*=" --type=ts -A 5
rg "interface\s+LLMResponse" --type=ts -A 5Repository: ThinkInAIXYZ/deepchat
Length of output: 529
🏁 Script executed:
#!/bin/bash
# Find the voiceAIProvider file and check generateSpeech implementation
find . -name "voiceAIProvider.ts" -type fRepository: ThinkInAIXYZ/deepchat
Length of output: 135
🏁 Script executed:
#!/bin/bash
# Search for generateSpeech method definition
rg "generateSpeech" --type=ts -B 2 -A 10 src/main/presenter/llmProviderPresenter/providers/Repository: ThinkInAIXYZ/deepchat
Length of output: 6033
🏁 Script executed:
#!/bin/bash
# Check other LLM provider implementations to understand the pattern
ls -la src/main/presenter/llmProviderPresenter/providers/Repository: ThinkInAIXYZ/deepchat
Length of output: 2551
🏁 Script executed:
#!/bin/bash
# Check if LLMResponse type extends or if there are variants
rg "LLMResponse" --type=ts src/shared/types/ -B 2 -A 8Repository: ThinkInAIXYZ/deepchat
Length of output: 1655
🏁 Script executed:
#!/bin/bash
# Check base class or interface that voiceAIProvider implements
rg "class VoiceAIProvider\|extends\|implements" --type=ts src/main/presenter/llmProviderPresenter/providers/voiceAIProvider.ts -A 2Repository: ThinkInAIXYZ/deepchat
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Check voiceAIProvider class definition
head -20 src/main/presenter/llmProviderPresenter/providers/voiceAIProvider.tsRepository: ThinkInAIXYZ/deepchat
Length of output: 631
🏁 Script executed:
#!/bin/bash
# Check if these methods are required by an interface or base class
rg "completions\|summaries\|generateText" --type=ts src/main/presenter/llmProviderPresenter/providers/voiceAIProvider.ts -B 1 | head -30Repository: ThinkInAIXYZ/deepchat
Length of output: 47
🏁 Script executed:
#!/bin/bash
# Check BaseLLMProvider to see what methods are required
rg "class BaseLLMProvider|abstract\|completions\|summaries\|generateText" --type=ts src/main/presenter/llmProviderPresenter/baseProvider.ts -B 1 -A 5Repository: ThinkInAIXYZ/deepchat
Length of output: 365
🏁 Script executed:
#!/bin/bash
# Look for how non-stream methods work in other providers
rg "public async completions" --type=ts src/main/presenter/llmProviderPresenter/providers/ -A 8 | head -50Repository: ThinkInAIXYZ/deepchat
Length of output: 2750
🏁 Script executed:
#!/bin/bash
# Check if voiceAIProvider should even have these non-streaming methods
# Look at summaries and generateText in other providers
rg "public async summaries|public async generateText" --type=ts src/main/presenter/llmProviderPresenter/providers/ -lRepository: ThinkInAIXYZ/deepchat
Length of output: 266
🏁 Script executed:
#!/bin/bash
# Check the full voiceAIProvider to understand design intent
wc -l src/main/presenter/llmProviderPresenter/providers/voiceAIProvider.ts
cat -n src/main/presenter/llmProviderPresenter/providers/voiceAIProvider.ts | head -100Repository: ThinkInAIXYZ/deepchat
Length of output: 3224
🏁 Script executed:
#!/bin/bash
# Check if these methods are abstract/required in BaseLLMProvider
ast-grep --pattern $'abstract class BaseLLMProvider {
$$$
abstract completions($_, $_): $_
$$$
}'
rg "abstract.*completions|abstract.*summaries|abstract.*generateText" --type=ts src/main/presenter/llmProviderPresenter/baseProvider.tsRepository: ThinkInAIXYZ/deepchat
Length of output: 135
🏁 Script executed:
#!/bin/bash
# Check the entirety of voiceAIProvider to see full implementation intent
sed -n '91,165p' src/main/presenter/llmProviderPresenter/providers/voiceAIProvider.tsRepository: ThinkInAIXYZ/deepchat
Length of output: 1807
Remove unnecessary generateSpeech() calls from completions, summaries, and generateText methods.
These methods call generateSpeech() but discard the returned audio data { audioBase64, mimeType }, then return only text content. The LLMResponse type cannot hold audio data—only coreStream() properly delivers audio via streaming events. These methods are abstract requirements from BaseLLMProvider but should not generate speech if they cannot return it. Either remove the generateSpeech() calls to avoid wasteful API calls, or reconsider whether this TTS provider should implement these text-only methods.
🤖 Prompt for AI Agents
In `@src/main/presenter/llmProviderPresenter/providers/voiceAIProvider.ts` around
lines 91 - 141, The methods completions, summaries, and generateText call
generateSpeech() but discard its {audioBase64, mimeType} result and only return
an LLMResponse with text; remove the unnecessary generateSpeech() calls to avoid
wasteful TTS API usage. Update the implementations of completions, summaries,
and generateText in voiceAIProvider.ts to only validate input, skip calling
generateSpeech(), and return the appropriate text (e.g., text,
buildShortTitle(text), or prompt) as before; keep generateSpeech() and
coreStream() for the streaming/audio paths if needed.
| mimeType?: string | ||
| } | ||
|
|
||
| const audioError = ref(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
audioError ref is declared but never set.
The audioError ref is used in the template (line 19) to display an error message, but it's never set to true anywhere in the component logic. This means the error message will never be displayed.
🐛 Suggested fix - add error handling to audio element
Either remove the unused error handling, or implement it properly:
+const handleAudioError = () => {
+ audioError.value = true
+}And in the template:
-<audio :src="audioSrc" controls class="w-full" />
+<audio :src="audioSrc" controls class="w-full" `@error`="handleAudioError" />🤖 Prompt for AI Agents
In `@src/renderer/src/components/message/MessageBlockAudio.vue` at line 64,
audioError is initialized but never updated, so the template error message never
appears; wire the component's audio element error handling to set
audioError.value = true on failure and clear it on successful load. Add an error
event listener on the audio DOM ref (or set `@error` on the <audio> element) to
set audioError.value = true, set audioError.value = false in the audio load/play
success handlers (e.g., canplay, play or your existing load/play functions), and
remove listeners in onBeforeUnmount; locate the const audioError = ref(false)
and the audio element/ref handlers to implement these changes.
| "voiceai": { | ||
| "title": "Voice.ai Text-to-Speech", | ||
| "description": "Generate speech from text. Voices appear in the model list below.", | ||
| "audioFormat": { | ||
| "label": "Audio Format", | ||
| "placeholder": "Select format", | ||
| "helper": "MP3 is recommended for most cases." | ||
| }, | ||
| "language": { | ||
| "label": "Language", | ||
| "placeholder": "e.g. en", | ||
| "helper": "Supported: en, ca, sv, es, fr, de, it, pt, pl, ru, nl." | ||
| }, | ||
| "model": { | ||
| "label": "TTS Model", | ||
| "placeholder": "voiceai-tts-v1-latest", | ||
| "helper": "See the Voice.ai docs for supported models." | ||
| }, | ||
| "agentId": { | ||
| "label": "Voice Agent ID", | ||
| "placeholder": "Paste voice agent ID", | ||
| "helper": "Required for real-time voice calls." | ||
| }, | ||
| "temperature": { | ||
| "label": "Temperature", | ||
| "helper": "Controls randomness (0-2)." | ||
| }, | ||
| "topP": { | ||
| "label": "Top P", | ||
| "helper": "Nucleus sampling (0-1)." | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify voiceai keys exist in all locale files
echo "Checking voiceai keys in all language files:"
echo ""
for locale in da-DK en-US fa-IR fr-FR he-IL ja-JP ko-KR pt-BR ru-RU zh-CN zh-HK zh-TW; do
file="src/renderer/src/i18n/$locale/settings.json"
if [ -f "$file" ]; then
if grep -q '"voiceai"' "$file"; then
echo "✓ $locale: voiceai keys present"
else
echo "✗ $locale: voiceai keys MISSING"
fi
else
echo "? $locale: file not found"
fi
doneRepository: ThinkInAIXYZ/deepchat
Length of output: 470
Add voiceai translations to all remaining language files.
The voiceai section is only present in en-US and zh-CN. According to i18n guidelines, translations must be added to ALL 12 language files: da-DK, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-HK, and zh-TW. Use en-US as the reference and maintain consistent key names across all locales.
🤖 Prompt for AI Agents
In `@src/renderer/src/i18n/en-US/settings.json` around lines 496 - 527, Add the
missing "voiceai" translations (same keys as in en-US: voiceai.title,
voiceai.description, voiceai.audioFormat.label, voiceai.audioFormat.placeholder,
voiceai.audioFormat.helper, voiceai.language.label,
voiceai.language.placeholder, voiceai.language.helper, voiceai.model.label,
voiceai.model.placeholder, voiceai.model.helper, voiceai.agentId.label,
voiceai.agentId.placeholder, voiceai.agentId.helper, voiceai.temperature.label,
voiceai.temperature.helper, voiceai.topP.label, voiceai.topP.helper) into each
of the remaining locale JSON files (da-DK, fa-IR, fr-FR, he-IL, ja-JP, ko-KR,
pt-BR, ru-RU, zh-HK, zh-TW) using the en-US values as the baseline, preserving
exact key names and JSON structure so all 12 locales contain an identical
"voiceai" section.
| "audio": { | ||
| "play": "播放", | ||
| "pause": "暂停" | ||
| }, | ||
| "call": { | ||
| "title": "Voice.ai 通话", | ||
| "description": "点击连接 Voice.ai 语音代理,接通电话后说话即可。", | ||
| "start": "开始语音通话", | ||
| "stop": "结束通话", | ||
| "end": "挂断" | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check which locale chat.json files have the audio and call keys
locales=(da-DK en-US fa-IR fr-FR he-IL ja-JP ko-KR pt-BR ru-RU zh-CN zh-HK zh-TW)
echo "=== Checking for 'audio' and 'call' keys in chat.json files ==="
echo ""
for locale in "${locales[@]}"; do
file="src/renderer/src/i18n/$locale/chat.json"
if [ -f "$file" ]; then
has_audio=$(grep -q '"audio"' "$file" && echo "✓" || echo "✗")
has_call=$(grep -q '"call"' "$file" && echo "✓" || echo "✗")
echo "$locale: audio=$has_audio, call=$has_call"
else
echo "$locale: FILE NOT FOUND"
fi
doneRepository: ThinkInAIXYZ/deepchat
Length of output: 405
Add audio and call translations to all remaining locale files.
The new keys added to zh-CN/chat.json are missing from 10 other locale files (da-DK, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-HK, zh-TW). Per guidelines, all new translations must be added to all 12 language files with consistent key names.
🤖 Prompt for AI Agents
In `@src/renderer/src/i18n/zh-CN/chat.json` around lines 101 - 111, The new
translation keys audio.play, audio.pause and call.title, call.description,
call.start, call.stop, call.end were added to zh-CN/chat.json but are missing
from the other locale chat files; add those exact keys (with appropriate
translated strings) to each of the remaining locale files: da-DK, fa-IR, fr-FR,
he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-HK and zh-TW so all 12 locales have the
same key structure under the "audio" and "call" objects (preserve key names and
JSON structure).
| "voiceai": { | ||
| "title": "Voice.ai 文字转语音", | ||
| "description": "将文本生成语音,voice 会在下方模型列表中展示。", | ||
| "audioFormat": { | ||
| "label": "音频格式", | ||
| "placeholder": "选择格式", | ||
| "helper": "多数场景推荐 MP3。" | ||
| }, | ||
| "language": { | ||
| "label": "语言", | ||
| "placeholder": "例如 en", | ||
| "helper": "支持语言:en, ca, sv, es, fr, de, it, pt, pl, ru, nl。" | ||
| }, | ||
| "model": { | ||
| "label": "TTS 模型", | ||
| "placeholder": "voiceai-tts-v1-latest", | ||
| "helper": "可选模型请查看 Voice.ai 文档。" | ||
| }, | ||
| "agentId": { | ||
| "label": "语音 Agent ID", | ||
| "placeholder": "粘贴 voice agent id", | ||
| "helper": "实时通话需要此 ID。" | ||
| }, | ||
| "temperature": { | ||
| "label": "温度", | ||
| "helper": "随机性参数(0-2)。" | ||
| }, | ||
| "topP": { | ||
| "label": "Top P", | ||
| "helper": "Nucleus 采样(0-1)。" | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if voiceai translation keys exist in all locale files
echo "=== Checking voiceai translation keys across all locales ==="
for locale in da-DK en-US fa-IR fr-FR he-IL ja-JP ko-KR pt-BR ru-RU zh-CN zh-HK zh-TW; do
settings_file="src/renderer/src/i18n/${locale}/settings.json"
if [ -f "$settings_file" ]; then
if grep -q '"voiceai"' "$settings_file"; then
echo "✓ $locale: voiceai keys present"
else
echo "✗ $locale: voiceai keys MISSING"
fi
else
echo "? $locale: settings.json not found"
fi
done
echo ""
echo "=== Listing all language directories ==="
ls -la src/renderer/src/i18n/ | grep "^d" | awk '{print $NF}' | grep -v "^\."Repository: ThinkInAIXYZ/deepchat
Length of output: 599
Add voiceai translations to 10 missing language files.
The voiceai translation keys are only present in en-US and zh-CN. Per coding guidelines, translations must exist in ALL 12 language files. Add the provider.voiceai.* keys to:
- da-DK, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-HK, zh-TW
Each locale requires the same key structure with appropriate language translations.
🤖 Prompt for AI Agents
In `@src/renderer/src/i18n/zh-CN/settings.json` around lines 397 - 428, Add the
missing provider.voiceai translation block (same key names as the existing
"voiceai" object: title, description, audioFormat.{label,placeholder,helper},
language.{label,placeholder,helper}, model.{label,placeholder,helper},
agentId.{label,placeholder,helper}, temperature.{label,helper},
topP.{label,helper}) to each of the 10 locale files: da-DK, fa-IR, fr-FR, he-IL,
ja-JP, ko-KR, pt-BR, ru-RU, zh-HK, zh-TW, keeping the exact structure used in
the current zh-CN snippet and providing appropriate localized strings for each
field (or accurate fallbacks if a translation is unavailable). Ensure keys are
placed under the same parent namespace (provider.voiceai or top-level "voiceai"
depending on project convention), match spelling/casing of existing keys (e.g.,
"audioFormat", "agentId"), and run the linter/locale validation to confirm no
missing or extra keys.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/renderer/src/i18n/ko-KR/settings.json`:
- Around line 329-360: Add the missing provider.voiceai block (with keys:
voiceai.title, voiceai.description,
voiceai.audioFormat.label/placeholder/helper,
voiceai.language.label/placeholder/helper,
voiceai.model.label/placeholder/helper,
voiceai.agentId.label/placeholder/helper, voiceai.temperature.label/helper, and
voiceai.topP.label/helper) to every locale file (da-DK, en-US, fa-IR, fr-FR,
he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-CN, zh-HK, zh-TW) using identical key
names and structure as shown in the ko-KR diff so all locales contain the same
keys and no runtime strings are missing.
🧹 Nitpick comments (2)
src/renderer/src/i18n/zh-HK/settings.json (1)
343-351: Consider using「智能體」instead of “Agent” in zh-HK labels/placeholders.This aligns with the Chinese-locale guideline for “Agents” terminology.
♻️ Suggested wording tweak
- "agentId": { - "label": "語音 Agent ID", - "placeholder": "貼上 voice agent id", - "helper": "即時通話需要此 ID。" - }, + "agentId": { + "label": "語音智能體 ID", + "placeholder": "貼上語音智能體 ID", + "helper": "即時通話需要此 ID。" + },Based on learnings: For Chinese translations, consider using '智能体' for 'Agents' instead of the English term.
src/renderer/src/i18n/pt-BR/settings.json (1)
348-351: Align the placeholder with the Portuguese label.Minor polish: the placeholder can mirror the Portuguese wording used in the label.
📝 Suggested tweak
- "agentId": { - "label": "ID do agente de voz", - "placeholder": "Cole o ID do voice agent", - "helper": "Necessário para chamadas de voz em tempo real." - }, + "agentId": { + "label": "ID do agente de voz", + "placeholder": "Cole o ID do agente de voz", + "helper": "Necessário para chamadas de voz em tempo real." + },
| "voiceai": { | ||
| "title": "Voice.ai 텍스트 음성 변환", | ||
| "description": "텍스트로부터 음성을 생성합니다. 음성은 아래 모델 목록에 표시됩니다.", | ||
| "audioFormat": { | ||
| "label": "오디오 형식", | ||
| "placeholder": "형식 선택", | ||
| "helper": "대부분의 경우 MP3를 권장합니다." | ||
| }, | ||
| "language": { | ||
| "label": "언어", | ||
| "placeholder": "예: en", | ||
| "helper": "지원: en, ca, sv, es, fr, de, it, pt, pl, ru, nl." | ||
| }, | ||
| "model": { | ||
| "label": "TTS 모델", | ||
| "placeholder": "voiceai-tts-v1-latest", | ||
| "helper": "지원되는 모델은 Voice.ai 문서를 참고하세요." | ||
| }, | ||
| "agentId": { | ||
| "label": "음성 에이전트 ID", | ||
| "placeholder": "voice agent id 붙여넣기", | ||
| "helper": "실시간 음성 통화에 필요합니다." | ||
| }, | ||
| "temperature": { | ||
| "label": "온도", | ||
| "helper": "무작위성을 제어합니다 (0-2)." | ||
| }, | ||
| "topP": { | ||
| "label": "Top P", | ||
| "helper": "Nucleus 샘플링 (0-1)." | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add the new provider.voiceai keys to all locale files.
The Voice.ai block appears only in ko-KR here; per i18n rules it must be added with identical keys to all supported locales (da-DK, en-US, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-CN, zh-HK, zh-TW) to avoid missing strings at runtime.
As per coding guidelines: “Add new translations to ALL language files (da-DK, en-US, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-CN, zh-HK, zh-TW) with consistent key names across all locales.”
🤖 Prompt for AI Agents
In `@src/renderer/src/i18n/ko-KR/settings.json` around lines 329 - 360, Add the
missing provider.voiceai block (with keys: voiceai.title, voiceai.description,
voiceai.audioFormat.label/placeholder/helper,
voiceai.language.label/placeholder/helper,
voiceai.model.label/placeholder/helper,
voiceai.agentId.label/placeholder/helper, voiceai.temperature.label/helper, and
voiceai.topP.label/helper) to every locale file (da-DK, en-US, fa-IR, fr-FR,
he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-CN, zh-HK, zh-TW) using identical key
names and structure as shown in the ko-KR diff so all locales contain the same
keys and no runtime strings are missing.
Summary by CodeRabbit
New Features
Localization
✏️ Tip: You can customize this high-level summary in your review settings.