feat(acp): migrate getDictationConfig and transcribeDictation to ACP (#10048)

This commit is contained in:
Abhijay Jain
2026-06-27 21:53:44 +05:30
committed by GitHub
parent 4f7bf8c1e2
commit b3c086e47a
3 changed files with 40 additions and 19 deletions
+22
View File
@@ -0,0 +1,22 @@
import type { DictationProviderStatusEntry } from '@aaif/goose-sdk';
import { getAcpClient } from './acpConnection';
export type { DictationProviderStatusEntry };
export type DictationProviders = Record<string, DictationProviderStatusEntry>;
export async function getDictationConfig(): Promise<DictationProviders> {
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<string> {
const client = await getAcpClient();
const response = await client.goose.dictationTranscribe_unstable({ audio, mimeType, provider });
return response.text;
}
@@ -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<DictationProvider | null>(null);
const [providerStatuses, setProviderStatuses] = useState<Record<string, DictationProviderStatus>>(
const [providerStatuses, setProviderStatuses] = useState<Record<string, DictationProviderStatusEntry>>(
{}
);
const [preferredMic, setPreferredMic] = useState<string | null>(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 = () => {
<div className="py-2 px-2">
<LocalModelManager />
</div>
) : providerStatuses[provider].uses_provider_config ? (
) : providerStatuses[provider].usesProviderConfig ? (
<div className="py-2 px-2 bg-background-secondary rounded-lg">
{!providerStatuses[provider].configured ? (
<p className="text-xs text-text-secondary">
{intl.formatMessage(i18n.configureApiKey, { settingsPath: providerStatuses[provider].settings_path, b: (chunks: React.ReactNode) => <b>{chunks}</b> })}
{intl.formatMessage(i18n.configureApiKey, { settingsPath: providerStatuses[provider].settingsPath, b: (chunks: React.ReactNode) => <b>{chunks}</b> })}
</p>
) : (
<p className="text-xs text-green-600">
{intl.formatMessage(i18n.configuredIn, { settingsPath: providerStatuses[provider].settings_path })}
{intl.formatMessage(i18n.configuredIn, { settingsPath: providerStatuses[provider].settingsPath })}
</p>
)}
</div>
+7 -9
View File
@@ -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));