Skip to content

Commit

Permalink
Merge pull request #125 from braden-w/feat/restore-auto-detect-langua…
Browse files Browse the repository at this point in the history
…ge-feature

feat: restore auto detect language feature
  • Loading branch information
braden-w authored Jun 29, 2024
2 parents 87a8f90 + 198e88d commit 1c3f89a
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 68 deletions.
2 changes: 1 addition & 1 deletion apps/app/src/lib/stores/settings.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const createSettings = Effect.gen(function* () {
currentLocalShortcut: registerShortcutsService.defaultLocalShortcut,
currentGlobalShortcut: registerShortcutsService.defaultGlobalShortcut,
apiKey: '',
outputLanguage: 'en',
outputLanguage: 'auto',
},
});

Expand Down
8 changes: 4 additions & 4 deletions packages/shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Schema as S } from '@effect/schema';
import { Data, Effect } from 'effect';
import { notificationOptionsSchema } from './services/NotificationService.js';
import { SUPPORTED_LANGUAGES } from './services/TranscriptionServiceWhisperingLive.js';

export const WHISPERING_URL =
process.env.NODE_ENV === 'production'
Expand All @@ -17,7 +18,7 @@ export const settingsSchema = S.Struct({
currentLocalShortcut: S.String,
currentGlobalShortcut: S.String,
apiKey: S.String,
outputLanguage: S.String,
outputLanguage: S.Literal(...SUPPORTED_LANGUAGES),
});

export type Settings = S.Schema.Type<typeof settingsSchema>;
Expand All @@ -35,9 +36,8 @@ export type WhisperingErrorProperties = {
error?: unknown;
};

export class WhisperingError extends Data.TaggedError(
'WhisperingError',
)<Required<Pick<WhisperingErrorProperties, 'variant'>> & Omit<WhisperingErrorProperties, 'variant'>
export class WhisperingError extends Data.TaggedError('WhisperingError')<
Required<Pick<WhisperingErrorProperties, 'variant'>> & Omit<WhisperingErrorProperties, 'variant'>
> {
constructor(properties: WhisperingErrorProperties) {
super({
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/services/TranscriptionService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { WhisperingError } from '@repo/shared';
import type { SupportedLanguage, WhisperingError } from '@repo/shared';
import type { Effect } from 'effect';
import { Context } from 'effect';

Expand All @@ -8,7 +8,7 @@ export class TranscriptionService extends Context.Tag('TranscriptionService')<
readonly supportedLanguages: readonly { label: string; value: string }[];
readonly transcribe: (
blob: Blob,
options: { apiKey: string; outputLanguage: string },
options: { apiKey: string; outputLanguage: SupportedLanguage },
) => Effect.Effect<string, WhisperingError>;
}
>() {}
192 changes: 131 additions & 61 deletions packages/shared/src/services/TranscriptionServiceWhisperingLive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WhisperingError } from '@repo/shared';
import { Effect, Layer } from 'effect';
import { Effect, Layer, Option } from 'effect';
import { TranscriptionService } from './TranscriptionService.js';

function isString(input: unknown): input is string {
Expand All @@ -10,70 +10,140 @@ const MAX_FILE_SIZE_MB = 25 as const;
const FILE_NAME = 'recording.wav';

/** Supported languages pulled from OpenAI Website: https://platform.openai.com/docs/guides/speech-to-text/supported-languages */
const SUPPORTED_LANGUAGES = [
{ label: 'Afrikaans', value: 'af' },
{ label: 'Arabic', value: 'ar' },
{ label: 'Armenian', value: 'hy' },
{ label: 'Azerbaijani', value: 'az' },
{ label: 'Belarusian', value: 'be' },
{ label: 'Bosnian', value: 'bs' },
{ label: 'Bulgarian', value: 'bg' },
{ label: 'Catalan', value: 'ca' },
{ label: 'Chinese', value: 'zh' },
{ label: 'Croatian', value: 'hr' },
{ label: 'Czech', value: 'cs' },
{ label: 'Danish', value: 'da' },
{ label: 'Dutch', value: 'nl' },
{ label: 'English', value: 'en' },
{ label: 'Estonian', value: 'et' },
{ label: 'Finnish', value: 'fi' },
{ label: 'French', value: 'fr' },
{ label: 'Galician', value: 'gl' },
{ label: 'German', value: 'de' },
{ label: 'Greek', value: 'el' },
{ label: 'Hebrew', value: 'he' },
{ label: 'Hindi', value: 'hi' },
{ label: 'Hungarian', value: 'hu' },
{ label: 'Icelandic', value: 'is' },
{ label: 'Indonesian', value: 'id' },
{ label: 'Italian', value: 'it' },
{ label: 'Japanese', value: 'ja' },
{ label: 'Kannada', value: 'kn' },
{ label: 'Kazakh', value: 'kk' },
{ label: 'Korean', value: 'ko' },
{ label: 'Latvian', value: 'lv' },
{ label: 'Lithuanian', value: 'lt' },
{ label: 'Macedonian', value: 'mk' },
{ label: 'Malay', value: 'ms' },
{ label: 'Marathi', value: 'mr' },
{ label: 'Maori', value: 'mi' },
{ label: 'Nepali', value: 'ne' },
{ label: 'Norwegian', value: 'no' },
{ label: 'Persian', value: 'fa' },
{ label: 'Polish', value: 'pl' },
{ label: 'Portuguese', value: 'pt' },
{ label: 'Romanian', value: 'ro' },
{ label: 'Russian', value: 'ru' },
{ label: 'Serbian', value: 'sr' },
{ label: 'Slovak', value: 'sk' },
{ label: 'Slovenian', value: 'sl' },
{ label: 'Spanish', value: 'es' },
{ label: 'Swahili', value: 'sw' },
{ label: 'Swedish', value: 'sv' },
{ label: 'Tagalog', value: 'tl' },
{ label: 'Tamil', value: 'ta' },
{ label: 'Thai', value: 'th' },
{ label: 'Turkish', value: 'tr' },
{ label: 'Ukrainian', value: 'uk' },
{ label: 'Urdu', value: 'ur' },
{ label: 'Vietnamese', value: 'vi' },
{ label: 'Welsh', value: 'cy' },
export const SUPPORTED_LANGUAGES = [
'auto',
'af',
'ar',
'hy',
'az',
'be',
'bs',
'bg',
'ca',
'zh',
'hr',
'cs',
'da',
'nl',
'en',
'et',
'fi',
'fr',
'gl',
'de',
'el',
'he',
'hi',
'hu',
'is',
'id',
'it',
'ja',
'kn',
'kk',
'ko',
'lv',
'lt',
'mk',
'ms',
'mr',
'mi',
'ne',
'no',
'fa',
'pl',
'pt',
'ro',
'ru',
'sr',
'sk',
'sl',
'es',
'sw',
'sv',
'tl',
'ta',
'th',
'tr',
'uk',
'ur',
'vi',
'cy',
] as const;

export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];

const SUPPORTED_LANGUAGES_TO_LABEL = {
auto: 'Auto',
af: 'Afrikaans',
ar: 'Arabic',
hy: 'Armenian',
az: 'Azerbaijani',
be: 'Belarusian',
bs: 'Bosnian',
bg: 'Bulgarian',
ca: 'Catalan',
zh: 'Chinese',
hr: 'Croatian',
cs: 'Czech',
da: 'Danish',
nl: 'Dutch',
en: 'English',
et: 'Estonian',
fi: 'Finnish',
fr: 'French',
gl: 'Galician',
de: 'German',
el: 'Greek',
he: 'Hebrew',
hi: 'Hindi',
hu: 'Hungarian',
is: 'Icelandic',
id: 'Indonesian',
it: 'Italian',
ja: 'Japanese',
kn: 'Kannada',
kk: 'Kazakh',
ko: 'Korean',
lv: 'Latvian',
lt: 'Lithuanian',
mk: 'Macedonian',
ms: 'Malay',
mr: 'Marathi',
mi: 'Maori',
ne: 'Nepali',
no: 'Norwegian',
fa: 'Persian',
pl: 'Polish',
pt: 'Portuguese',
ro: 'Romanian',
ru: 'Russian',
sr: 'Serbian',
sk: 'Slovak',
sl: 'Slovenian',
es: 'Spanish',
sw: 'Swahili',
sv: 'Swedish',
tl: 'Tagalog',
ta: 'Tamil',
th: 'Thai',
tr: 'Turkish',
uk: 'Ukrainian',
ur: 'Urdu',
vi: 'Vietnamese',
cy: 'Welsh',
} as const satisfies Record<SupportedLanguage, string>;

export const TranscriptionServiceWhisperLive = Layer.succeed(
TranscriptionService,
TranscriptionService.of({
supportedLanguages: SUPPORTED_LANGUAGES,
supportedLanguages: SUPPORTED_LANGUAGES.map(
(lang) =>
({
label: SUPPORTED_LANGUAGES_TO_LABEL[lang],
value: lang,
}) as const,
),
transcribe: (audioBlob, { apiKey, outputLanguage }) =>
Effect.gen(function* () {
if (!apiKey.startsWith('sk-')) {
Expand All @@ -97,7 +167,7 @@ export const TranscriptionServiceWhisperLive = Layer.succeed(
const formData = new FormData();
formData.append('file', wavFile);
formData.append('model', 'whisper-1');
formData.append('language', outputLanguage);
if (outputLanguage !== 'auto') formData.append('language', outputLanguage);
const data = yield* Effect.tryPromise({
try: () =>
fetch('https://api.openai.com/v1/audio/transcriptions', {
Expand Down

0 comments on commit 1c3f89a

Please sign in to comment.