diff --git a/ui/desktop/src/acp/dictation.ts b/ui/desktop/src/acp/dictation.ts new file mode 100644 index 000000000..4901ea30e --- /dev/null +++ b/ui/desktop/src/acp/dictation.ts @@ -0,0 +1,22 @@ +import type { DictationProviderStatusEntry } from '@aaif/goose-sdk'; +import { getAcpClient } from './acpConnection'; + +export type { DictationProviderStatusEntry }; + +export type DictationProviders = Record; + +export async function getDictationConfig(): Promise { + const client = await getAcpClient(); + const response = await client.goose.dictationConfig_unstable({}); + return response.providers ?? {}; +} + +export async function transcribeDictation( + audio: string, + mimeType: string, + provider: string +): Promise { + const client = await getAcpClient(); + const response = await client.goose.dictationTranscribe_unstable({ audio, mimeType, provider }); + return response.text; +} diff --git a/ui/desktop/src/components/settings/dictation/DictationSettings.tsx b/ui/desktop/src/components/settings/dictation/DictationSettings.tsx index 94a020866..31025ca3d 100644 --- a/ui/desktop/src/components/settings/dictation/DictationSettings.tsx +++ b/ui/desktop/src/components/settings/dictation/DictationSettings.tsx @@ -1,6 +1,7 @@ import { useState, useEffect } from 'react'; import { ChevronDown } from 'lucide-react'; -import { DictationProvider, getDictationConfig, DictationProviderStatus } from '../../../api'; +import { DictationProvider } from '../../../api'; +import { getDictationConfig, DictationProviderStatusEntry } from '../../../acp/dictation'; import { useConfig } from '../../ConfigContext'; import { Input } from '../../ui/input'; import { Button } from '../../ui/button'; @@ -85,7 +86,7 @@ export const DictationSettings = () => { const intl = useIntl(); const { localInference, isLoading: isFeaturesLoading } = useFeatures(); const [provider, setProvider] = useState(null); - const [providerStatuses, setProviderStatuses] = useState>( + const [providerStatuses, setProviderStatuses] = useState>( {} ); const [preferredMic, setPreferredMic] = useState(null); @@ -95,7 +96,7 @@ export const DictationSettings = () => { const refreshStatuses = async () => { const audioConfig = await getDictationConfig(); - setProviderStatuses(audioConfig.data || {}); + setProviderStatuses(audioConfig); }; useEffect(() => { @@ -145,12 +146,12 @@ export const DictationSettings = () => { const handleSaveKey = async () => { if (!provider) return; const providerConfig = providerStatuses[provider]; - if (!providerConfig || providerConfig.uses_provider_config) return; + if (!providerConfig || providerConfig.usesProviderConfig) return; const trimmedKey = apiKey.trim(); if (!trimmedKey) return; - const keyName = providerConfig.config_key!; + const keyName = providerConfig.configKey!; await upsert(keyName, trimmedKey, true); setApiKey(''); setIsEditingKey(false); @@ -160,9 +161,9 @@ export const DictationSettings = () => { const handleRemoveKey = async () => { if (!provider) return; const providerConfig = providerStatuses[provider]; - if (!providerConfig || providerConfig.uses_provider_config) return; + if (!providerConfig || providerConfig.usesProviderConfig) return; - const keyName = providerConfig.config_key!; + const keyName = providerConfig.configKey!; await remove(keyName, true); setApiKey(''); setIsEditingKey(false); @@ -222,15 +223,15 @@ export const DictationSettings = () => {
- ) : providerStatuses[provider].uses_provider_config ? ( + ) : providerStatuses[provider].usesProviderConfig ? (
{!providerStatuses[provider].configured ? (

- {intl.formatMessage(i18n.configureApiKey, { settingsPath: providerStatuses[provider].settings_path, b: (chunks: React.ReactNode) => {chunks} })} + {intl.formatMessage(i18n.configureApiKey, { settingsPath: providerStatuses[provider].settingsPath, b: (chunks: React.ReactNode) => {chunks} })}

) : (

- {intl.formatMessage(i18n.configuredIn, { settingsPath: providerStatuses[provider].settings_path })} + {intl.formatMessage(i18n.configuredIn, { settingsPath: providerStatuses[provider].settingsPath })}

)}
diff --git a/ui/desktop/src/hooks/useAudioRecorder.ts b/ui/desktop/src/hooks/useAudioRecorder.ts index 579b30998..4886529b7 100644 --- a/ui/desktop/src/hooks/useAudioRecorder.ts +++ b/ui/desktop/src/hooks/useAudioRecorder.ts @@ -1,5 +1,6 @@ import { useState, useRef, useCallback, useEffect } from 'react'; -import { transcribeDictation, getDictationConfig, DictationProvider } from '../api'; +import { DictationProvider } from '../api'; +import { getDictationConfig, transcribeDictation } from '../acp/dictation'; import { useConfig } from '../components/ConfigContext'; import { errorMessage } from '../utils/conversionUtils'; @@ -99,8 +100,8 @@ export const useAudioRecorder = ({ onTranscription, onError }: UseAudioRecorderO setProvider(null); return; } - const resp = await getDictationConfig(); - setIsEnabled(!!resp.data?.[pref]?.configured); + const providers = await getDictationConfig(); + setIsEnabled(!!providers[pref]?.configured); setProvider(pref); } catch (error) { console.error('Failed to check dictation config:', error); @@ -121,12 +122,9 @@ export const useAudioRecorder = ({ onTranscription, onError }: UseAudioRecorderO try { const wav = new Blob([encodeWav(samples, SAMPLE_RATE)], { type: 'audio/wav' }); const base64 = await blobToBase64(wav); - const result = await transcribeDictation({ - body: { audio: base64, mime_type: 'audio/wav', provider: prov }, - throwOnError: true, - }); - if (result.data?.text) { - onTranscriptionRef.current(result.data.text); + const text = await transcribeDictation(base64, 'audio/wav', prov); + if (text) { + onTranscriptionRef.current(text); } } catch (error) { onErrorRef.current(errorMessage(error));