@ai-sdk/fish-audio 0.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/LICENSE +13 -0
- package/README.md +61 -3
- package/dist/index.d.ts +176 -0
- package/dist/index.js +569 -0
- package/dist/index.js.map +1 -0
- package/docs/190-fish-audio.mdx +346 -0
- package/package.json +71 -15
- package/src/fish-audio-config.ts +8 -0
- package/src/fish-audio-error.ts +17 -0
- package/src/fish-audio-provider.ts +163 -0
- package/src/fish-audio-speech-api-types.ts +126 -0
- package/src/fish-audio-speech-model-options.ts +115 -0
- package/src/fish-audio-speech-model.ts +297 -0
- package/src/fish-audio-speech-options.ts +21 -0
- package/src/fish-audio-transcription-model-options.ts +28 -0
- package/src/fish-audio-transcription-model.ts +166 -0
- package/src/fish-audio-transcription-options.ts +11 -0
- package/src/index.ts +15 -0
- package/src/version.ts +6 -0
- package/index.js +0 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { SharedV4Warning, TranscriptionModelV4 } from '@ai-sdk/provider';
|
|
2
|
+
import {
|
|
3
|
+
combineHeaders,
|
|
4
|
+
convertBase64ToUint8Array,
|
|
5
|
+
createJsonResponseHandler,
|
|
6
|
+
mediaTypeToExtension,
|
|
7
|
+
parseProviderOptions,
|
|
8
|
+
postFormDataToApi,
|
|
9
|
+
serializeModelOptions,
|
|
10
|
+
WORKFLOW_DESERIALIZE,
|
|
11
|
+
WORKFLOW_SERIALIZE,
|
|
12
|
+
} from '@ai-sdk/provider-utils';
|
|
13
|
+
import { z } from 'zod/v4';
|
|
14
|
+
import type { FishAudioConfig } from './fish-audio-config';
|
|
15
|
+
import { fishAudioFailedResponseHandler } from './fish-audio-error';
|
|
16
|
+
import { fishAudioTranscriptionModelOptionsSchema } from './fish-audio-transcription-model-options';
|
|
17
|
+
import type { FishAudioTranscriptionModelId } from './fish-audio-transcription-options';
|
|
18
|
+
|
|
19
|
+
interface FishAudioTranscriptionModelConfig extends FishAudioConfig {
|
|
20
|
+
_internal?: {
|
|
21
|
+
currentDate?: () => Date;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class FishAudioTranscriptionModel implements TranscriptionModelV4 {
|
|
26
|
+
readonly specificationVersion = 'v4';
|
|
27
|
+
|
|
28
|
+
get provider(): string {
|
|
29
|
+
return this.config.provider;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static [WORKFLOW_SERIALIZE](model: FishAudioTranscriptionModel) {
|
|
33
|
+
return serializeModelOptions({
|
|
34
|
+
modelId: model.modelId,
|
|
35
|
+
config: model.config,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
40
|
+
modelId: FishAudioTranscriptionModelId;
|
|
41
|
+
config: FishAudioTranscriptionModelConfig;
|
|
42
|
+
}) {
|
|
43
|
+
return new FishAudioTranscriptionModel(options.modelId, options.config);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
constructor(
|
|
47
|
+
readonly modelId: FishAudioTranscriptionModelId,
|
|
48
|
+
private readonly config: FishAudioTranscriptionModelConfig,
|
|
49
|
+
) {}
|
|
50
|
+
|
|
51
|
+
private async getArgs({
|
|
52
|
+
audio,
|
|
53
|
+
mediaType,
|
|
54
|
+
providerOptions,
|
|
55
|
+
}: Parameters<TranscriptionModelV4['doGenerate']>[0]) {
|
|
56
|
+
const warnings: SharedV4Warning[] = [];
|
|
57
|
+
|
|
58
|
+
const fishAudioOptions = await parseProviderOptions({
|
|
59
|
+
provider: 'fishAudio',
|
|
60
|
+
providerOptions,
|
|
61
|
+
schema: fishAudioTranscriptionModelOptionsSchema,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const formData = new FormData();
|
|
65
|
+
const blob =
|
|
66
|
+
audio instanceof Uint8Array
|
|
67
|
+
? new Blob([audio])
|
|
68
|
+
: new Blob([convertBase64ToUint8Array(audio)]);
|
|
69
|
+
|
|
70
|
+
formData.append(
|
|
71
|
+
'audio',
|
|
72
|
+
new File([blob], 'audio', { type: mediaType }),
|
|
73
|
+
`audio.${mediaTypeToExtension(mediaType)}`,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (fishAudioOptions?.language != null) {
|
|
77
|
+
formData.append('language', fishAudioOptions.language);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Fish Audio defaults `ignore_timestamps` to true, which leaves the
|
|
81
|
+
// transcription result without segments. Request timestamps by default and
|
|
82
|
+
// let callers opt back out.
|
|
83
|
+
formData.append(
|
|
84
|
+
'ignore_timestamps',
|
|
85
|
+
String(fishAudioOptions?.ignoreTimestamps ?? false),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return { formData, warnings };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async doGenerate(
|
|
92
|
+
options: Parameters<TranscriptionModelV4['doGenerate']>[0],
|
|
93
|
+
): Promise<Awaited<ReturnType<TranscriptionModelV4['doGenerate']>>> {
|
|
94
|
+
const currentDate = this.config._internal?.currentDate?.() ?? new Date();
|
|
95
|
+
const { formData, warnings } = await this.getArgs(options);
|
|
96
|
+
|
|
97
|
+
const {
|
|
98
|
+
value: response,
|
|
99
|
+
responseHeaders,
|
|
100
|
+
rawValue: rawResponse,
|
|
101
|
+
} = await postFormDataToApi({
|
|
102
|
+
url: this.config.url({ path: '/v1/asr', modelId: this.modelId }),
|
|
103
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
104
|
+
formData,
|
|
105
|
+
failedResponseHandler: fishAudioFailedResponseHandler,
|
|
106
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
107
|
+
fishAudioTranscriptionResponseSchema,
|
|
108
|
+
),
|
|
109
|
+
abortSignal: options.abortSignal,
|
|
110
|
+
fetch: this.config.fetch,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const segments =
|
|
114
|
+
response.segments?.map(segment => ({
|
|
115
|
+
text: segment.text,
|
|
116
|
+
startSecond: segment.start,
|
|
117
|
+
endSecond: segment.end,
|
|
118
|
+
})) ?? [];
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
text: response.text,
|
|
122
|
+
segments,
|
|
123
|
+
// `language_code` is absent from the documented response schema but is
|
|
124
|
+
// returned in practice, and reflects the detected language rather than
|
|
125
|
+
// the requested one.
|
|
126
|
+
language: response.language_code ?? undefined,
|
|
127
|
+
durationInSeconds: response.duration ?? undefined,
|
|
128
|
+
warnings,
|
|
129
|
+
response: {
|
|
130
|
+
timestamp: currentDate,
|
|
131
|
+
modelId: this.modelId,
|
|
132
|
+
headers: responseHeaders,
|
|
133
|
+
body: rawResponse,
|
|
134
|
+
},
|
|
135
|
+
...(response.language != null && {
|
|
136
|
+
providerMetadata: {
|
|
137
|
+
fishAudio: {
|
|
138
|
+
// Human-readable display name, e.g. `English`. Its exact form is
|
|
139
|
+
// not guaranteed, so `language` above (the ISO-639-1 code) is the
|
|
140
|
+
// value to branch on.
|
|
141
|
+
language: response.language,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const fishAudioTranscriptionResponseSchema = z.object({
|
|
150
|
+
text: z.string(),
|
|
151
|
+
// `language` and `language_code` are undocumented but returned in practice.
|
|
152
|
+
// Human-readable name, e.g. `English`.
|
|
153
|
+
language: z.string().nullish(),
|
|
154
|
+
// ISO-639-1 code, e.g. `en`.
|
|
155
|
+
language_code: z.string().nullish(),
|
|
156
|
+
duration: z.number().nullish(),
|
|
157
|
+
segments: z
|
|
158
|
+
.array(
|
|
159
|
+
z.object({
|
|
160
|
+
text: z.string(),
|
|
161
|
+
start: z.number(),
|
|
162
|
+
end: z.number(),
|
|
163
|
+
}),
|
|
164
|
+
)
|
|
165
|
+
.nullish(),
|
|
166
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fish Audio transcription model ID.
|
|
3
|
+
*
|
|
4
|
+
* `POST /v1/asr` currently exposes no model selector and serves a single
|
|
5
|
+
* model, so `transcribe-1` is a routing label rather than a wire value: it is
|
|
6
|
+
* not sent to the API. Fish Audio expects to add more ASR models and to select
|
|
7
|
+
* them with the `model` HTTP header, matching `/v1/tts`.
|
|
8
|
+
*
|
|
9
|
+
* https://docs.fish.audio/api-reference/endpoint/openapi-v1/speech-to-text
|
|
10
|
+
*/
|
|
11
|
+
export type FishAudioTranscriptionModelId = 'transcribe-1' | (string & {});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { createFishAudio, fishAudio } from './fish-audio-provider';
|
|
2
|
+
export type {
|
|
3
|
+
FishAudioProvider,
|
|
4
|
+
FishAudioProviderSettings,
|
|
5
|
+
} from './fish-audio-provider';
|
|
6
|
+
export { FishAudioSpeechModel } from './fish-audio-speech-model';
|
|
7
|
+
export { FishAudioTranscriptionModel } from './fish-audio-transcription-model';
|
|
8
|
+
export type {
|
|
9
|
+
FishAudioSpeechModelId,
|
|
10
|
+
FishAudioSpeechVoiceId,
|
|
11
|
+
} from './fish-audio-speech-options';
|
|
12
|
+
export type { FishAudioSpeechModelOptions } from './fish-audio-speech-model-options';
|
|
13
|
+
export type { FishAudioTranscriptionModelId } from './fish-audio-transcription-options';
|
|
14
|
+
export type { FishAudioTranscriptionModelOptions } from './fish-audio-transcription-model-options';
|
|
15
|
+
export { VERSION } from './version';
|
package/src/version.ts
ADDED
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|