feat(generate-transcription): opt-in verbose mode (#159)
* feat(generate-transcription): opt-in verbose mode * chore(generate-transcription): update test * chore(generate-transcription): clean snapshot
This commit is contained in:
1 parent
341d2b098a
commit
942de6be35
3 files changed
+59
-17
No files matched your search
@@ -2,24 +2,42 @@ import type { CommonRequestOptions } from '@xsai/shared'
|
||||
|
||||
import { requestHeaders, requestURL, responseJSON } from '@xsai/shared'
|
||||
|
||||
export interface GenerateTranscriptionOptions<T extends GenerateTranscriptionOptionsTimeStampGranularities = undefined> extends CommonRequestOptions {
|
||||
export interface GenerateTranscriptionOptions<
|
||||
T1 extends GenerateTranscriptionOptionsResponseFormat,
|
||||
T2 extends GenerateTranscriptionOptionsTimeStampGranularities,
|
||||
> extends CommonRequestOptions {
|
||||
file: Blob
|
||||
fileName?: string
|
||||
language?: string
|
||||
prompt?: string
|
||||
/** @default `json` */
|
||||
responseFormat?: T1
|
||||
temperature?: string
|
||||
/** @default `segment` */
|
||||
timestampGranularities?: T
|
||||
timestampGranularities?: T2
|
||||
}
|
||||
|
||||
export type GenerateTranscriptionOptionsResponseFormat = 'json' | 'verbose_json' | undefined
|
||||
|
||||
export type GenerateTranscriptionOptionsTimeStampGranularities = 'segment' | 'word' | undefined
|
||||
|
||||
export interface GenerateTranscriptionResult<T extends GenerateTranscriptionOptionsTimeStampGranularities = undefined> {
|
||||
duration: number
|
||||
language: string
|
||||
segments: T extends 'word' ? never : GenerateTranscriptionResultSegment[]
|
||||
export interface GenerateTranscriptionResult<
|
||||
T1 extends GenerateTranscriptionOptionsResponseFormat,
|
||||
T2 extends GenerateTranscriptionOptionsTimeStampGranularities,
|
||||
> {
|
||||
duration: T1 extends 'verbose_json' ? number : never
|
||||
language: T1 extends 'verbose_json' ? string : never
|
||||
segments: T1 extends 'verbose_json'
|
||||
? T2 extends 'word'
|
||||
? never
|
||||
: GenerateTranscriptionResultSegment[]
|
||||
: never
|
||||
text: string
|
||||
words: T extends 'word' ? GenerateTranscriptionResultWord[] : never
|
||||
words: T1 extends 'verbose_json'
|
||||
? T2 extends 'word'
|
||||
? GenerateTranscriptionResultWord[]
|
||||
: never
|
||||
: never
|
||||
}
|
||||
|
||||
/** @see {@link https://platform.openai.com/docs/api-reference/audio/verbose-json-object#audio/verbose-json-object-segments} */
|
||||
@@ -43,13 +61,20 @@ export interface GenerateTranscriptionResultWord {
|
||||
word: string
|
||||
}
|
||||
|
||||
export const generateTranscription = async <T extends GenerateTranscriptionOptionsTimeStampGranularities = undefined>(options: GenerateTranscriptionOptions<T>): Promise<GenerateTranscriptionResult<T>> => {
|
||||
export const generateTranscription = async <
|
||||
T1 extends GenerateTranscriptionOptionsResponseFormat = undefined,
|
||||
T2 extends GenerateTranscriptionOptionsTimeStampGranularities = undefined,
|
||||
>(options: GenerateTranscriptionOptions<T1, T2>): Promise<GenerateTranscriptionResult<T1, T2>> => {
|
||||
const body = new FormData()
|
||||
|
||||
body.append('model', options.model)
|
||||
body.append('file', options.file, options.fileName)
|
||||
body.append('response_format', 'verbose_json')
|
||||
body.append('timestamp_granularities[]', options.timestampGranularities ?? 'segment')
|
||||
body.append('response_format', options.responseFormat ?? 'json')
|
||||
|
||||
// make ts happy
|
||||
// eslint-disable-next-line ts/no-unnecessary-type-assertion
|
||||
if (options.responseFormat as GenerateTranscriptionOptionsResponseFormat === 'verbose_json')
|
||||
body.append('timestamp_granularities[]', options.timestampGranularities ?? 'segment')
|
||||
|
||||
if (options.language != null)
|
||||
body.append('language', options.language)
|
||||
@@ -66,5 +91,5 @@ export const generateTranscription = async <T extends GenerateTranscriptionOptio
|
||||
method: 'POST',
|
||||
signal: options.abortSignal,
|
||||
})
|
||||
.then(responseJSON<GenerateTranscriptionResult<T>>)
|
||||
.then(responseJSON<GenerateTranscriptionResult<T1, T2>>)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`@xsai/generate-transcription > segment 1`] = `
|
||||
exports[`@xsai/generate-transcription > verbose_json + segment 1`] = `
|
||||
[
|
||||
{
|
||||
"avg_logprob": -0.084375,
|
||||
@@ -43,7 +43,7 @@ exports[`@xsai/generate-transcription > segment 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`@xsai/generate-transcription > word 1`] = `
|
||||
exports[`@xsai/generate-transcription > verbose_json + word 1`] = `
|
||||
[
|
||||
{
|
||||
"end": 0.4,
|
||||
|
||||
@@ -4,7 +4,22 @@ import { describe, expect, it } from 'vitest'
|
||||
import { generateTranscription } from '../src'
|
||||
|
||||
describe('@xsai/generate-transcription', () => {
|
||||
it('segment', async () => {
|
||||
const expectText = 'Hello, I am your AI assistant. Just let me know how I can help bring your ideas to life.'
|
||||
|
||||
it('json', async () => {
|
||||
const { text } = await generateTranscription({
|
||||
apiKey: 'a',
|
||||
baseURL: new URL('http://localhost:8000/v1/'),
|
||||
file: await openAsBlob('./test/fixtures/basic.wav', { type: 'audio/wav' }),
|
||||
fileName: 'basic.wav',
|
||||
language: 'en',
|
||||
model: 'deepdml/faster-whisper-large-v3-turbo-ct2',
|
||||
})
|
||||
|
||||
expect(text).toBe(expectText)
|
||||
})
|
||||
|
||||
it('verbose_json + segment', async () => {
|
||||
const { duration, language, segments, text } = await generateTranscription({
|
||||
apiKey: 'a',
|
||||
baseURL: new URL('http://localhost:8000/v1/'),
|
||||
@@ -12,15 +27,16 @@ describe('@xsai/generate-transcription', () => {
|
||||
fileName: 'basic.wav',
|
||||
language: 'en',
|
||||
model: 'deepdml/faster-whisper-large-v3-turbo-ct2',
|
||||
responseFormat: 'verbose_json',
|
||||
})
|
||||
|
||||
expect(duration).toBe(5.472)
|
||||
expect(language).toBe('en')
|
||||
expect(text).toBe('Hello, I am your AI assistant. Just let me know how I can help bring your ideas to life.')
|
||||
expect(text).toBe(expectText)
|
||||
expect(segments).toMatchSnapshot()
|
||||
}, 30000)
|
||||
|
||||
it('word', async () => {
|
||||
it('verbose_json + word', async () => {
|
||||
const { duration, language, text, words } = await generateTranscription({
|
||||
apiKey: 'a',
|
||||
baseURL: new URL('http://localhost:8000/v1/'),
|
||||
@@ -28,12 +44,13 @@ describe('@xsai/generate-transcription', () => {
|
||||
fileName: 'basic.wav',
|
||||
language: 'en',
|
||||
model: 'deepdml/faster-whisper-large-v3-turbo-ct2',
|
||||
responseFormat: 'verbose_json',
|
||||
timestampGranularities: 'word',
|
||||
})
|
||||
|
||||
expect(duration).toBe(5.472)
|
||||
expect(language).toBe('en')
|
||||
expect(text).toBe('Hello, I am your AI assistant. Just let me know how I can help bring your ideas to life.')
|
||||
expect(text).toBe(expectText)
|
||||
expect(words).toMatchSnapshot()
|
||||
}, 30000)
|
||||
})
|
||||
Reference in new issue
Block a user