Files
makelore/electron/services/learning-speech-client.ts
inman 01bee3188b
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled
feat: integrate learning module
2026-08-16 20:52:16 +08:00

53 lines
2.3 KiB
TypeScript

import { WORKS_SQUARE_CONFIG } from '../api/works-config';
import { proxyAwareFetch } from '../utils/proxy-fetch';
import { getValidWorksSquareAccessToken } from './works-square-session';
const MAX_AUDIO_BYTES = 26_214_400;
export type LearningSpeechRequest = {
audio: Uint8Array;
fileName: string;
mimeType: string;
language?: string;
};
type Dependencies = {
fetchImpl?: typeof fetch;
getAccessToken?: typeof getValidWorksSquareAccessToken;
apiBaseUrl?: string;
};
export function createLearningSpeechClient(dependencies: Dependencies = {}) {
const fetchImpl = dependencies.fetchImpl ?? proxyAwareFetch;
const getAccessToken = dependencies.getAccessToken ?? getValidWorksSquareAccessToken;
const apiBaseUrl = (dependencies.apiBaseUrl ?? WORKS_SQUARE_CONFIG.apiBaseUrl).replace(/\/+$/, '');
async function transcribe(input: LearningSpeechRequest): Promise<{ text: string }> {
const audio = input.audio instanceof Uint8Array ? input.audio : new Uint8Array(input.audio);
if (!audio.byteLength || audio.byteLength > MAX_AUDIO_BYTES) throw new Error('语音长度不符合要求');
const token = await getAccessToken({ fetchImpl });
if (!token) throw new Error('请先登录');
const form = new FormData();
form.set('audio', new Blob([audio], { type: input.mimeType || 'audio/webm' }), input.fileName || 'voice.webm');
if (input.language?.trim()) form.set('language', input.language.trim());
const request = (accessToken: string) => fetchImpl(`${apiBaseUrl}/api/speech/transcriptions`, {
method: 'POST',
headers: { Authorization: `Bearer ${accessToken}` },
body: form,
});
let response = await request(token);
if (response.status === 401) {
await response.body?.cancel().catch(() => undefined);
const refreshed = await getAccessToken({ fetchImpl, forceRefresh: true });
if (refreshed) response = await request(refreshed);
}
const payload = await response.json().catch(() => null) as { text?: unknown; detail?: unknown } | null;
if (!response.ok || typeof payload?.text !== 'string' || !payload.text.trim()) {
throw new Error(typeof payload?.detail === 'string' ? payload.detail : '语音识别失败');
}
return { text: payload.text.trim() };
}
return { transcribe };
}