api2key-base-sdk 0.1.4 → 0.1.6

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/README.md CHANGED
@@ -16,7 +16,8 @@
16
16
  6. `orders`
17
17
  7. `payment`
18
18
  8. `ai`
19
- 9. `admin`
19
+ 9. `speech`
20
+ 10. `admin`
20
21
 
21
22
  安装原则:
22
23
 
@@ -36,7 +37,8 @@
36
37
  6. `orders`
37
38
  7. `payment`
38
39
  8. `ai`
39
- 9. `admin`
40
+ 9. `speech`
41
+ 10. `admin`
40
42
 
41
43
  ### 1. `auth`
42
44
 
@@ -46,6 +48,12 @@
46
48
 
47
49
  1. `login(input)`
48
50
  2. `register(input)`
51
+ 3. `verifyEmail(input)`
52
+ 4. `resendVerification(input)`
53
+ 5. `forgotPassword(input)`
54
+ 6. `resetPassword(input)`
55
+ 7. `sendChangePasswordCode(accessToken?)`
56
+ 8. `changePassword(input, accessToken?)`
49
57
  3. `me(accessToken?)`
50
58
  4. `logout(accessToken?)`
51
59
 
@@ -70,9 +78,18 @@
70
78
 
71
79
  负责用户级设置的读取与更新。
72
80
 
73
- ### 5. `projects / orders / payment / ai`
81
+ ### 5. `projects / orders / payment / ai / speech`
74
82
 
75
- 负责项目商品、会员、订单、支付、AI 等运行时能力。
83
+ 负责项目商品、会员、订单、支付、AI 以及语音运行时能力。
84
+
85
+ 其中 `speech` 对齐当前 `api2key-base-api` 的内置语音接口:
86
+
87
+ 1. `listVoices(...)`
88
+ 2. `synthesize(...)`
89
+ 3. `transcribe(...)`
90
+ 4. `transcribeSrt(...)`
91
+ 5. `getAsrTask(...)`
92
+ 6. `downloadAudio(...)`
76
93
 
77
94
  ### 6. `admin`
78
95
 
@@ -108,9 +125,25 @@ const baseClient = createBasePlatformClient({
108
125
  const me = await baseClient.auth.me();
109
126
  const credits = await baseClient.credits.getBalance();
110
127
 
128
+ await baseClient.auth.verifyEmail({
129
+ email: 'user@example.com',
130
+ code: '123456',
131
+ });
132
+
133
+ await baseClient.auth.forgotPassword({
134
+ email: 'user@example.com',
135
+ });
136
+
137
+ await baseClient.auth.resetPassword({
138
+ email: 'user@example.com',
139
+ code: '123456',
140
+ newPassword: 'NewPassword123!',
141
+ });
142
+
111
143
  const projectClient = createProjectPlatformClient({
112
144
  baseUrl: process.env.API2KEY_BASE_URL!,
113
145
  getAccessToken: () => process.env.ACCESS_TOKEN,
146
+ getApiKey: () => process.env.API_KEY,
114
147
  getProjectId: () => process.env.PROJECT_ID,
115
148
  });
116
149
 
@@ -120,9 +153,185 @@ const adminClient = createAdminPlatformClient({
120
153
  });
121
154
 
122
155
  const products = await projectClient.projects.getCatalogProducts(process.env.PROJECT_ID!);
156
+ const voices = await projectClient.speech.listVoices({
157
+ provider: 'azure',
158
+ locale: 'zh-CN',
159
+ });
123
160
  const projects = await adminClient.admin.listProjects();
124
161
  ```
125
162
 
163
+ 说明:
164
+
165
+ 1. SDK 仍然接收单一 `baseUrl`
166
+ 2. `/api/v1/*` 接口继续基于 `baseUrl` 访问
167
+ 3. `speech` 客户端会自动基于同一域名访问根路径下的 `/api/*` 语音接口,无需额外配置第二个 URL
168
+
169
+ ## Speech 示例
170
+
171
+ 推荐在项目级客户端上使用:
172
+
173
+ ```ts
174
+ import { createProjectPlatformClient } from 'api2key-base-sdk';
175
+
176
+ const client = createProjectPlatformClient({
177
+ baseUrl: process.env.API2KEY_BASE_URL!,
178
+ getAccessToken: () => process.env.ACCESS_TOKEN,
179
+ getApiKey: () => process.env.API_KEY,
180
+ getProjectId: () => process.env.PROJECT_ID,
181
+ });
182
+ ```
183
+
184
+ ### 1. 获取声音列表
185
+
186
+ ```ts
187
+ const voices = await client.speech.listVoices({
188
+ provider: 'azure',
189
+ locale: 'zh-CN',
190
+ });
191
+
192
+ console.log(voices.provider);
193
+ console.log(voices.total);
194
+ console.log(voices.voices[0]?.shortName);
195
+ ```
196
+
197
+ ### 2. 文本转音频
198
+
199
+ ```ts
200
+ const synthesized = await client.speech.synthesize({
201
+ provider: 'azure',
202
+ text: '你好,欢迎使用 api2key speech client。',
203
+ locale: 'zh-CN',
204
+ voice: 'zh-CN-XiaoxiaoNeural',
205
+ format: 'audio-24khz-96kbitrate-mono-mp3',
206
+ rate: 1,
207
+ volume: 100,
208
+ pitch: 0,
209
+ });
210
+
211
+ console.log(synthesized.contentType);
212
+ console.log(synthesized.creditsCharged);
213
+ console.log(synthesized.storageKey);
214
+ console.log(synthesized.downloadUrl);
215
+ console.log(synthesized.audio.byteLength);
216
+ ```
217
+
218
+ 如果你要把返回音频写入文件,在 Node.js 中可以这样处理:
219
+
220
+ ```ts
221
+ import { writeFile } from 'node:fs/promises';
222
+
223
+ await writeFile('speech.mp3', Buffer.from(synthesized.audio));
224
+ ```
225
+
226
+ ### 3. 简洁音频转文本
227
+
228
+ ```ts
229
+ import { readFile } from 'node:fs/promises';
230
+
231
+ const audioBytes = await readFile('./sample.mp3');
232
+ const audioBlob = new Blob([audioBytes], { type: 'audio/mpeg' });
233
+
234
+ const transcript = await client.speech.transcribe({
235
+ provider: 'groq',
236
+ file: audioBlob,
237
+ fileName: 'sample.mp3',
238
+ });
239
+
240
+ console.log(transcript.language);
241
+ console.log(transcript.full_text);
242
+ console.log(transcript.segments.length);
243
+ ```
244
+
245
+ 也可以直接传远程地址:
246
+
247
+ ```ts
248
+ const transcript = await client.speech.transcribe({
249
+ provider: 'groq',
250
+ audioUrl: 'https://example.com/sample.mp3',
251
+ });
252
+ ```
253
+
254
+ ### 4. 音频转 SRT
255
+
256
+ 同步模式:
257
+
258
+ ```ts
259
+ const srtResult = await client.speech.transcribeSrt({
260
+ provider: 'tencent',
261
+ file: audioBlob,
262
+ fileName: 'sample.mp3',
263
+ async: false,
264
+ });
265
+
266
+ if ('srt' in srtResult) {
267
+ console.log(srtResult.text);
268
+ console.log(srtResult.srt);
269
+ }
270
+ ```
271
+
272
+ 异步模式:
273
+
274
+ ```ts
275
+ const submitted = await client.speech.transcribeSrt({
276
+ provider: 'tencent',
277
+ file: audioBlob,
278
+ fileName: 'sample.mp3',
279
+ async: true,
280
+ });
281
+
282
+ if ('taskId' in submitted && !('srt' in submitted)) {
283
+ console.log(submitted.taskId);
284
+ console.log(submitted.statusStr);
285
+ }
286
+ ```
287
+
288
+ ### 5. 查询异步字幕任务
289
+
290
+ ```ts
291
+ const task = await client.speech.getAsrTask({
292
+ provider: 'tencent',
293
+ taskId: 123456,
294
+ });
295
+
296
+ console.log(task.status);
297
+ console.log(task.statusStr);
298
+ console.log(task.retryAfterSeconds);
299
+ console.log(task.srt);
300
+ ```
301
+
302
+ ### 6. 通过存储 key 下载合成音频
303
+
304
+ ```ts
305
+ const downloaded = await client.speech.downloadAudio({
306
+ key: synthesized.storageKey!,
307
+ });
308
+
309
+ console.log(downloaded.contentType);
310
+ console.log(downloaded.fileName);
311
+ console.log(downloaded.audio.byteLength);
312
+ ```
313
+
314
+ ### 7. 鉴权建议
315
+
316
+ 推荐方式:
317
+
318
+ 1. 服务端项目调用时,通过 `getApiKey()` 提供 `sk-...`
319
+ 2. 用户登录态场景,通过 `getAccessToken()` 提供 JWT
320
+ 3. 大多数项目建议同时提供 `getProjectId()`,保持语音接口和项目上下文一致
321
+
322
+ ### 8. 路径说明
323
+
324
+ 你只需要传一个 `baseUrl`,例如:
325
+
326
+ ```ts
327
+ baseUrl: 'https://open.api2key.com/api/v1'
328
+ ```
329
+
330
+ SDK 会自动处理:
331
+
332
+ 1. 普通平台接口继续访问 `/api/v1/*`
333
+ 2. speech client 自动访问同域名下的 `/api/*`
334
+
126
335
  ## 设计原则
127
336
 
128
337
  1. 新项目默认只依赖这个包
@@ -1,5 +1,5 @@
1
1
  import { PlatformHttpClient } from '../core/client';
2
- import type { AuthSession, AuthUser } from '../core/types';
2
+ import type { AuthChangePasswordInput, AuthPasswordResetDispatchResult, AuthRegisterResult, AuthSession, AuthUser, AuthVerifyEmailInput, AuthResetPasswordInput } from '../core/types';
3
3
  export declare class AuthClient {
4
4
  private readonly http;
5
5
  constructor(http: PlatformHttpClient);
@@ -13,10 +13,20 @@ export declare class AuthClient {
13
13
  password: string;
14
14
  name?: string;
15
15
  projectId?: string;
16
- }): Promise<AuthSession>;
16
+ }): Promise<AuthRegisterResult>;
17
+ verifyEmail(input: AuthVerifyEmailInput): Promise<AuthSession>;
18
+ resendVerification(input: {
19
+ email: string;
20
+ }): Promise<null>;
21
+ forgotPassword(input: {
22
+ email: string;
23
+ }): Promise<AuthPasswordResetDispatchResult | null>;
24
+ resetPassword(input: AuthResetPasswordInput): Promise<null>;
17
25
  me(accessToken?: string): Promise<{
18
26
  user: AuthUser;
19
27
  membership?: unknown;
20
28
  }>;
21
29
  logout(accessToken?: string): Promise<null>;
30
+ sendChangePasswordCode(accessToken?: string): Promise<AuthPasswordResetDispatchResult>;
31
+ changePassword(input: AuthChangePasswordInput, accessToken?: string): Promise<null>;
22
32
  }
@@ -14,6 +14,30 @@ export class AuthClient {
14
14
  body: input,
15
15
  });
16
16
  }
17
+ verifyEmail(input) {
18
+ return this.http.request('/api/v1/auth/verify-email', {
19
+ method: 'POST',
20
+ body: input,
21
+ });
22
+ }
23
+ resendVerification(input) {
24
+ return this.http.request('/api/v1/auth/resend-verification', {
25
+ method: 'POST',
26
+ body: input,
27
+ });
28
+ }
29
+ forgotPassword(input) {
30
+ return this.http.request('/api/v1/auth/forgot-password', {
31
+ method: 'POST',
32
+ body: input,
33
+ });
34
+ }
35
+ resetPassword(input) {
36
+ return this.http.request('/api/v1/auth/reset-password', {
37
+ method: 'POST',
38
+ body: input,
39
+ });
40
+ }
17
41
  me(accessToken) {
18
42
  return this.http.request('/api/v1/auth/me', {
19
43
  accessToken,
@@ -25,4 +49,17 @@ export class AuthClient {
25
49
  accessToken,
26
50
  });
27
51
  }
52
+ sendChangePasswordCode(accessToken) {
53
+ return this.http.request('/api/v1/auth/change-password/send-code', {
54
+ method: 'POST',
55
+ accessToken,
56
+ });
57
+ }
58
+ changePassword(input, accessToken) {
59
+ return this.http.request('/api/v1/auth/change-password', {
60
+ method: 'POST',
61
+ body: input,
62
+ accessToken,
63
+ });
64
+ }
28
65
  }
@@ -4,6 +4,8 @@ export declare class PlatformHttpClient {
4
4
  private readonly fetchImpl;
5
5
  private readonly options;
6
6
  constructor(options: PlatformClientOptions);
7
+ private resolveBaseUrl;
8
+ private buildUrl;
7
9
  private send;
8
10
  request<T>(path: string, requestOptions?: PlatformRequestOptions): Promise<T>;
9
11
  requestRaw(path: string, requestOptions?: PlatformRequestOptions): Promise<Response>;
@@ -18,9 +18,29 @@ export class PlatformHttpClient {
18
18
  const fetchImpl = options.fetchImpl ?? fetch;
19
19
  this.fetchImpl = fetchImpl.bind(globalThis);
20
20
  }
21
+ resolveBaseUrl(mode = 'default') {
22
+ if (mode === 'origin') {
23
+ return new URL(this.baseUrl).origin;
24
+ }
25
+ return this.baseUrl;
26
+ }
27
+ buildUrl(path, requestOptions) {
28
+ if (/^https?:\/\//.test(path)) {
29
+ return `${path}${buildQueryString(requestOptions.query)}`;
30
+ }
31
+ return `${this.resolveBaseUrl(requestOptions.baseUrlMode)}${path}${buildQueryString(requestOptions.query)}`;
32
+ }
21
33
  async send(path, requestOptions = {}) {
22
34
  const headers = new Headers(this.options.defaultHeaders);
23
- headers.set('Content-Type', 'application/json');
35
+ if (requestOptions.contentType === null) {
36
+ headers.delete('Content-Type');
37
+ }
38
+ else if (requestOptions.contentType) {
39
+ headers.set('Content-Type', requestOptions.contentType);
40
+ }
41
+ else if (requestOptions.rawBody === undefined) {
42
+ headers.set('Content-Type', 'application/json');
43
+ }
24
44
  if (requestOptions.headers) {
25
45
  for (const [key, value] of Object.entries(requestOptions.headers)) {
26
46
  headers.set(key, value);
@@ -30,6 +50,10 @@ export class PlatformHttpClient {
30
50
  if (accessToken) {
31
51
  headers.set('Authorization', `Bearer ${accessToken}`);
32
52
  }
53
+ const apiKey = requestOptions.apiKey ?? (await this.options.getApiKey?.());
54
+ if (apiKey) {
55
+ headers.set('x-api-key', apiKey);
56
+ }
33
57
  const projectId = requestOptions.projectId ?? (await this.options.getProjectId?.());
34
58
  if (projectId) {
35
59
  headers.set('X-Project-Id', projectId);
@@ -41,13 +65,13 @@ export class PlatformHttpClient {
41
65
  }
42
66
  headers.set('X-Service-Secret', serviceSecret);
43
67
  }
44
- const url = `${this.baseUrl}${path}${buildQueryString(requestOptions.query)}`;
68
+ const url = this.buildUrl(path, requestOptions);
45
69
  let response;
46
70
  try {
47
71
  response = await this.fetchImpl(url, {
48
72
  method: requestOptions.method ?? 'GET',
49
73
  headers,
50
- body: requestOptions.body == null ? undefined : JSON.stringify(requestOptions.body),
74
+ body: requestOptions.rawBody ?? (requestOptions.body == null ? undefined : JSON.stringify(requestOptions.body)),
51
75
  });
52
76
  }
53
77
  catch (error) {
@@ -9,17 +9,22 @@ export interface PlatformClientOptions {
9
9
  fetchImpl?: typeof fetch;
10
10
  defaultHeaders?: Record<string, string>;
11
11
  getAccessToken?: () => MaybePromise<string | undefined>;
12
+ getApiKey?: () => MaybePromise<string | undefined>;
12
13
  getServiceSecret?: () => MaybePromise<string | undefined>;
13
14
  getProjectId?: () => MaybePromise<string | undefined>;
14
15
  }
15
16
  export interface PlatformRequestOptions {
16
17
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
17
18
  body?: unknown;
19
+ rawBody?: BodyInit;
18
20
  query?: Record<string, string | number | boolean | undefined | null>;
19
21
  headers?: Record<string, string>;
22
+ contentType?: string | null;
20
23
  accessToken?: string;
24
+ apiKey?: string;
21
25
  projectId?: string;
22
26
  useServiceSecret?: boolean;
27
+ baseUrlMode?: 'default' | 'origin';
23
28
  }
24
29
  export interface AuthUser {
25
30
  id: string;
@@ -27,12 +32,16 @@ export interface AuthUser {
27
32
  name?: string;
28
33
  avatar?: string | null;
29
34
  role: 'user' | 'admin' | 'super_admin';
35
+ emailVerified?: boolean;
36
+ emailVerifiedAt?: number | null;
37
+ credits?: number;
30
38
  creditsBalance?: number;
31
39
  projectId?: string | null;
32
40
  projectName?: string | null;
33
41
  projectSlug?: string | null;
34
42
  membership?: {
35
43
  tier: string;
44
+ status?: string;
36
45
  power?: number;
37
46
  endDate?: number | null;
38
47
  } | null;
@@ -56,6 +65,36 @@ export interface AuthSession {
56
65
  refreshToken?: string;
57
66
  expiresIn?: number;
58
67
  }
68
+ export interface AuthRegistrationPending {
69
+ requiresEmailVerification: true;
70
+ email: string;
71
+ verificationExpiresIn: number;
72
+ }
73
+ export type AuthRegisterResult = AuthSession | AuthRegistrationPending;
74
+ export type AuthVerifyEmailInput = {
75
+ token: string;
76
+ } | {
77
+ email: string;
78
+ code: string;
79
+ };
80
+ export interface AuthVerificationDispatchResult {
81
+ email: string;
82
+ verificationExpiresIn: number;
83
+ }
84
+ export interface AuthPasswordResetDispatchResult {
85
+ email: string;
86
+ resetExpiresIn: number;
87
+ }
88
+ export interface AuthResetPasswordInput {
89
+ email: string;
90
+ code: string;
91
+ newPassword: string;
92
+ }
93
+ export interface AuthChangePasswordInput {
94
+ currentPassword: string;
95
+ newPassword: string;
96
+ code: string;
97
+ }
59
98
  export interface ProjectSummary {
60
99
  id: string;
61
100
  name: string;
@@ -93,10 +132,7 @@ export interface MembershipView {
93
132
  days_remaining?: number;
94
133
  }
95
134
  export interface CreditsBalance {
96
- balance: number;
97
- reserved?: number;
98
- total_earned?: number;
99
- total_spent?: number;
135
+ user: AuthUser;
100
136
  }
101
137
  export interface CreditsLedgerItem {
102
138
  id: string;
@@ -187,6 +223,174 @@ export interface AiModelSummary {
187
223
  outputPricing?: number;
188
224
  locked?: boolean;
189
225
  }
226
+ export type SpeechTtsProvider = 'azure' | 'tencent';
227
+ export type SpeechAsrProvider = 'tencent' | 'groq' | 'bcut';
228
+ export interface SpeechVoice {
229
+ provider: SpeechTtsProvider;
230
+ shortName: string;
231
+ displayName: string;
232
+ localName: string;
233
+ locale: string;
234
+ localeName: string;
235
+ gender: string;
236
+ voiceType?: string;
237
+ styles: string[];
238
+ roles: string[];
239
+ sampleRateHertz?: string;
240
+ sampleRates?: string[];
241
+ status?: string;
242
+ wordsPerMinute?: string;
243
+ secondaryLocales: string[];
244
+ recommendedScene?: string;
245
+ supportedLanguages?: string[];
246
+ emotions?: string[];
247
+ }
248
+ export interface SpeechAccessUser {
249
+ id: string;
250
+ email: string;
251
+ role: string;
252
+ }
253
+ export interface SpeechAccessProject {
254
+ id: string;
255
+ name: string;
256
+ slug: string;
257
+ }
258
+ export interface SpeechAccessMembership {
259
+ tier: string;
260
+ status: string;
261
+ end_date: number | null;
262
+ is_active: boolean;
263
+ }
264
+ export interface SpeechVoiceListResult {
265
+ provider: SpeechTtsProvider;
266
+ user: SpeechAccessUser;
267
+ project: SpeechAccessProject;
268
+ membership: SpeechAccessMembership;
269
+ total: number;
270
+ voices: SpeechVoice[];
271
+ }
272
+ export interface SpeechSynthesisRequest {
273
+ provider?: SpeechTtsProvider;
274
+ text: string;
275
+ voice?: string;
276
+ locale?: string;
277
+ rate?: number;
278
+ volume?: number;
279
+ pitch?: number;
280
+ style?: string;
281
+ styleDegree?: number;
282
+ role?: string;
283
+ emotionCategory?: string;
284
+ emotionIntensity?: number;
285
+ format?: string;
286
+ }
287
+ export interface SpeechSynthesisResult {
288
+ audio: ArrayBuffer;
289
+ contentType: string;
290
+ fileName: string | null;
291
+ provider: string | null;
292
+ voice: string | null;
293
+ locale: string | null;
294
+ format: string | null;
295
+ creditsCharged: number | null;
296
+ storageKey: string | null;
297
+ downloadUrl: string | null;
298
+ projectId: string | null;
299
+ providerRequestId: string | null;
300
+ headers: Headers;
301
+ }
302
+ export interface SpeechSimpleTranscriptSegment {
303
+ start: number;
304
+ end: number;
305
+ text: string;
306
+ }
307
+ export interface SpeechTranscribeResult {
308
+ language: string;
309
+ full_text: string;
310
+ segments: SpeechSimpleTranscriptSegment[];
311
+ srt: string;
312
+ }
313
+ export interface SpeechAsrSegment {
314
+ id: number;
315
+ startMs: number;
316
+ endMs: number;
317
+ startTimecode: string;
318
+ endTimecode: string;
319
+ text: string;
320
+ speakerId?: number;
321
+ emotionTypes?: string[];
322
+ }
323
+ export interface SpeechAsrSrtResult {
324
+ user: SpeechAccessUser;
325
+ project: SpeechAccessProject;
326
+ membership: SpeechAccessMembership;
327
+ provider: SpeechAsrProvider;
328
+ format: 'srt';
329
+ engineModelType: string;
330
+ taskId: number | null;
331
+ requestId?: string | null;
332
+ language: string;
333
+ durationMs: number;
334
+ sourceType: 'file' | 'url';
335
+ sourceName?: string | null;
336
+ sourceSize?: number | null;
337
+ text: string;
338
+ srt: string;
339
+ segments: SpeechAsrSegment[];
340
+ }
341
+ export interface SpeechAsrTaskSubmission {
342
+ user: SpeechAccessUser;
343
+ project: SpeechAccessProject;
344
+ membership: SpeechAccessMembership;
345
+ provider: SpeechAsrProvider;
346
+ taskId: number;
347
+ requestId?: string | null;
348
+ engineModelType: string;
349
+ status: number;
350
+ statusStr: string;
351
+ sourceType: 'file' | 'url';
352
+ sourceName?: string | null;
353
+ sourceSize?: number | null;
354
+ }
355
+ export interface SpeechAsrTaskResult {
356
+ user: SpeechAccessUser;
357
+ project: SpeechAccessProject;
358
+ membership: SpeechAccessMembership;
359
+ provider: SpeechAsrProvider;
360
+ format: 'srt';
361
+ taskId: number;
362
+ requestId?: string | null;
363
+ status: number;
364
+ statusStr: string;
365
+ retryAfterSeconds?: number | null;
366
+ durationMs: number;
367
+ language: string;
368
+ audioDurationSeconds?: number | null;
369
+ errorMessage?: string | null;
370
+ text: string;
371
+ srt: string;
372
+ segments: SpeechAsrSegment[];
373
+ }
374
+ export interface SpeechAsrRequestInput {
375
+ provider?: SpeechAsrProvider;
376
+ audioUrl?: string;
377
+ file?: Blob;
378
+ fileName?: string;
379
+ async?: boolean;
380
+ language?: string;
381
+ prompt?: string;
382
+ temperature?: number;
383
+ engineModelType?: string;
384
+ channelNum?: number;
385
+ speakerDiarization?: number;
386
+ speakerNumber?: number;
387
+ sentenceMaxLength?: number;
388
+ emotionRecognition?: number;
389
+ filterDirty?: number;
390
+ filterModal?: number;
391
+ filterPunc?: number;
392
+ convertNumMode?: number;
393
+ }
190
394
  export interface AdminProject {
191
395
  id: string;
192
396
  name: string;
@@ -1,9 +1,13 @@
1
1
  import { PlatformHttpClient } from '../core/client';
2
2
  import type { CreditsBalance, CreditsLedgerItem } from '../core/types';
3
+ type GetBalanceOptions = {
4
+ accessToken?: string;
5
+ projectId?: string;
6
+ };
3
7
  export declare class CreditsClient {
4
8
  private readonly http;
5
9
  constructor(http: PlatformHttpClient);
6
- getBalance(accessToken?: string): Promise<CreditsBalance>;
10
+ getBalance(options?: string | GetBalanceOptions): Promise<CreditsBalance>;
7
11
  getLedger(input: {
8
12
  accessToken?: string;
9
13
  page?: number;
@@ -38,3 +42,4 @@ export declare class CreditsClient {
38
42
  ok: boolean;
39
43
  }>;
40
44
  }
45
+ export {};
@@ -2,8 +2,17 @@ export class CreditsClient {
2
2
  constructor(http) {
3
3
  this.http = http;
4
4
  }
5
- getBalance(accessToken) {
6
- return this.http.request('/api/v1/credits/balance', { accessToken });
5
+ getBalance(options) {
6
+ const normalizedOptions = typeof options === 'string'
7
+ ? { accessToken: options }
8
+ : (options ?? {});
9
+ return this.http.request('/api/v1/credits/balance', {
10
+ accessToken: normalizedOptions.accessToken,
11
+ projectId: normalizedOptions.projectId,
12
+ query: {
13
+ projectId: normalizedOptions.projectId,
14
+ },
15
+ });
7
16
  }
8
17
  getLedger(input) {
9
18
  return this.http.request('/api/v1/credits/ledger', {
package/dist/index.d.ts CHANGED
@@ -7,10 +7,11 @@ import { CreditsClient } from './credits/client';
7
7
  import { OrdersClient } from './orders/client';
8
8
  import { PaymentClient } from './payment/client';
9
9
  import { ProjectsClient } from './projects/client';
10
+ import { SpeechClient } from './speech/client';
10
11
  import { UserApiKeysClient } from './user/api-keys-client';
11
12
  import { UserSettingsClient } from './user/settings-client';
12
13
  export * from './core/errors';
13
- export type { AdminConfigItem, AdminMembershipFilters, AdminMembershipRecord, AdminModelInput, AdminModelRecord, AdminOrderFilters, AdminOrderRecord, AdminProduct, AdminProductBackfillResult, AdminProductListResult, AdminProject, AdminProjectExportFormat, AdminUserCreditsSummary, AdminUserRecord, AiModelSummary, AuthSession, AuthUser, CreditsBalance, CreditsLedgerItem, MembershipView, OrderDetail, OrderSummary, PaymentCreateResponse, PaymentQueryResponse, PlatformClientOptions, PlatformEnvelope, PlatformRequestOptions, ProductSummary, ProjectSummary, UpdateAdminUserInput, UserApiKeyRecord, UserApiKeySecret, UserSettingDefinition, UserSettingsPayload, } from './core/types';
14
+ export type { AdminConfigItem, AdminMembershipFilters, AdminMembershipRecord, AdminModelInput, AdminModelRecord, AdminOrderFilters, AdminOrderRecord, AdminProduct, AdminProductBackfillResult, AdminProductListResult, AdminProject, AdminProjectExportFormat, AdminUserCreditsSummary, AdminUserRecord, AiModelSummary, AuthChangePasswordInput, AuthPasswordResetDispatchResult, AuthRegisterResult, AuthRegistrationPending, AuthSession, AuthUser, AuthVerificationDispatchResult, AuthVerifyEmailInput, AuthResetPasswordInput, CreditsBalance, CreditsLedgerItem, MembershipView, OrderDetail, OrderSummary, PaymentCreateResponse, PaymentQueryResponse, PlatformClientOptions, PlatformEnvelope, PlatformRequestOptions, ProductSummary, ProjectSummary, SpeechAccessMembership, SpeechAccessProject, SpeechAccessUser, SpeechAsrProvider, SpeechAsrRequestInput, SpeechAsrSegment, SpeechAsrSrtResult, SpeechAsrTaskResult, SpeechAsrTaskSubmission, SpeechSimpleTranscriptSegment, SpeechSynthesisRequest, SpeechSynthesisResult, SpeechTranscribeResult, SpeechTtsProvider, SpeechVoice, SpeechVoiceListResult, UpdateAdminUserInput, UserApiKeyRecord, UserApiKeySecret, UserSettingDefinition, UserSettingsPayload, } from './core/types';
14
15
  export declare class BasePlatformClient {
15
16
  readonly http: PlatformHttpClient;
16
17
  readonly auth: AuthClient;
@@ -26,6 +27,7 @@ export declare class ProjectPlatformClient {
26
27
  readonly orders: OrdersClient;
27
28
  readonly payment: PaymentClient;
28
29
  readonly ai: AiClient;
30
+ readonly speech: SpeechClient;
29
31
  constructor(options: PlatformClientOptions);
30
32
  }
31
33
  export declare function createProjectPlatformClient(options: PlatformClientOptions): ProjectPlatformClient;
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { CreditsClient } from './credits/client.js';
6
6
  import { OrdersClient } from './orders/client.js';
7
7
  import { PaymentClient } from './payment/client.js';
8
8
  import { ProjectsClient } from './projects/client.js';
9
+ import { SpeechClient } from './speech/client.js';
9
10
  import { UserApiKeysClient } from './user/api-keys-client.js';
10
11
  import { UserSettingsClient } from './user/settings-client.js';
11
12
  export * from './core/errors.js';
@@ -28,6 +29,7 @@ export class ProjectPlatformClient {
28
29
  this.orders = new OrdersClient(this.http);
29
30
  this.payment = new PaymentClient(this.http);
30
31
  this.ai = new AiClient(this.http);
32
+ this.speech = new SpeechClient(this.http);
31
33
  }
32
34
  }
33
35
  export function createProjectPlatformClient(options) {
@@ -0,0 +1,32 @@
1
+ import { PlatformHttpClient } from '../core/client';
2
+ import type { SpeechAsrRequestInput, SpeechAsrSrtResult, SpeechAsrTaskResult, SpeechAsrTaskSubmission, SpeechSynthesisRequest, SpeechSynthesisResult, SpeechTranscribeResult, SpeechVoiceListResult } from '../core/types';
3
+ type SpeechRequestAuth = {
4
+ accessToken?: string;
5
+ apiKey?: string;
6
+ projectId?: string;
7
+ };
8
+ export declare class SpeechClient {
9
+ private readonly http;
10
+ constructor(http: PlatformHttpClient);
11
+ listVoices(input?: SpeechRequestAuth & {
12
+ provider?: 'azure' | 'tencent';
13
+ locale?: string;
14
+ search?: string;
15
+ }): Promise<SpeechVoiceListResult>;
16
+ synthesize(input: SpeechRequestAuth & SpeechSynthesisRequest): Promise<SpeechSynthesisResult>;
17
+ transcribe(input: SpeechRequestAuth & SpeechAsrRequestInput): Promise<SpeechTranscribeResult>;
18
+ transcribeSrt(input: SpeechRequestAuth & SpeechAsrRequestInput): Promise<SpeechAsrSrtResult | SpeechAsrTaskSubmission>;
19
+ getAsrTask(input: SpeechRequestAuth & {
20
+ taskId: number | string;
21
+ provider?: 'tencent' | 'groq' | 'bcut';
22
+ }): Promise<SpeechAsrTaskResult>;
23
+ downloadAudio(input: {
24
+ key: string;
25
+ }): Promise<{
26
+ audio: ArrayBuffer;
27
+ contentType: string;
28
+ fileName: string | null;
29
+ headers: Headers;
30
+ }>;
31
+ }
32
+ export {};
@@ -0,0 +1,169 @@
1
+ import { PlatformApiError, PlatformTransportError } from '../core/errors.js';
2
+ function parseContentDispositionFileName(value) {
3
+ if (!value) {
4
+ return null;
5
+ }
6
+ const utf8Match = value.match(/filename\*=UTF-8''([^;]+)/i);
7
+ if (utf8Match) {
8
+ try {
9
+ return decodeURIComponent(utf8Match[1]);
10
+ }
11
+ catch {
12
+ return utf8Match[1];
13
+ }
14
+ }
15
+ const plainMatch = value.match(/filename="?([^";]+)"?/i);
16
+ return plainMatch?.[1] ?? null;
17
+ }
18
+ async function throwPlatformError(response) {
19
+ let payload = null;
20
+ try {
21
+ payload = (await response.json());
22
+ }
23
+ catch {
24
+ const detail = await response.text().catch(() => '');
25
+ throw new PlatformTransportError(`Platform API returned a non-JSON error response for ${response.url}: ${detail}`);
26
+ }
27
+ throw new PlatformApiError(payload.message, response.status, payload.code, payload.data);
28
+ }
29
+ function buildAsrFormData(input) {
30
+ const formData = new FormData();
31
+ const scalarEntries = [
32
+ ['provider', input.provider],
33
+ ['audioUrl', input.audioUrl],
34
+ ['async', input.async],
35
+ ['language', input.language],
36
+ ['prompt', input.prompt],
37
+ ['temperature', input.temperature],
38
+ ['engineModelType', input.engineModelType],
39
+ ['channelNum', input.channelNum],
40
+ ['speakerDiarization', input.speakerDiarization],
41
+ ['speakerNumber', input.speakerNumber],
42
+ ['sentenceMaxLength', input.sentenceMaxLength],
43
+ ['emotionRecognition', input.emotionRecognition],
44
+ ['filterDirty', input.filterDirty],
45
+ ['filterModal', input.filterModal],
46
+ ['filterPunc', input.filterPunc],
47
+ ['convertNumMode', input.convertNumMode],
48
+ ];
49
+ for (const [key, value] of scalarEntries) {
50
+ if (value == null) {
51
+ continue;
52
+ }
53
+ formData.append(key, String(value));
54
+ }
55
+ if (input.file) {
56
+ formData.append('file', input.file, input.fileName ?? 'audio');
57
+ }
58
+ return formData;
59
+ }
60
+ export class SpeechClient {
61
+ constructor(http) {
62
+ this.http = http;
63
+ }
64
+ listVoices(input = {}) {
65
+ return this.http.request('/api/voices', {
66
+ accessToken: input.accessToken,
67
+ apiKey: input.apiKey,
68
+ baseUrlMode: 'origin',
69
+ query: {
70
+ projectId: input.projectId,
71
+ provider: input.provider,
72
+ locale: input.locale,
73
+ search: input.search,
74
+ },
75
+ });
76
+ }
77
+ async synthesize(input) {
78
+ const response = await this.http.requestRaw('/api/speech', {
79
+ method: 'POST',
80
+ accessToken: input.accessToken,
81
+ apiKey: input.apiKey,
82
+ baseUrlMode: 'origin',
83
+ query: { projectId: input.projectId },
84
+ body: {
85
+ provider: input.provider,
86
+ text: input.text,
87
+ voice: input.voice,
88
+ locale: input.locale,
89
+ rate: input.rate,
90
+ volume: input.volume,
91
+ pitch: input.pitch,
92
+ style: input.style,
93
+ styleDegree: input.styleDegree,
94
+ role: input.role,
95
+ emotionCategory: input.emotionCategory,
96
+ emotionIntensity: input.emotionIntensity,
97
+ format: input.format,
98
+ },
99
+ });
100
+ if (!response.ok) {
101
+ return throwPlatformError(response);
102
+ }
103
+ return {
104
+ audio: await response.arrayBuffer(),
105
+ contentType: response.headers.get('Content-Type') ?? 'application/octet-stream',
106
+ fileName: parseContentDispositionFileName(response.headers.get('Content-Disposition')),
107
+ provider: response.headers.get('X-TTS-Provider'),
108
+ voice: response.headers.get('X-TTS-Voice'),
109
+ locale: response.headers.get('X-TTS-Locale'),
110
+ format: response.headers.get('X-TTS-Format'),
111
+ creditsCharged: response.headers.get('X-TTS-Credits-Charged') == null
112
+ ? null
113
+ : Number(response.headers.get('X-TTS-Credits-Charged')),
114
+ storageKey: response.headers.get('X-TTS-Storage-Key'),
115
+ downloadUrl: response.headers.get('X-TTS-Download-Url'),
116
+ projectId: response.headers.get('X-TTS-Project-Id'),
117
+ providerRequestId: response.headers.get('X-TTS-Provider-Request-Id'),
118
+ headers: response.headers,
119
+ };
120
+ }
121
+ transcribe(input) {
122
+ return this.http.request('/api/asr/transcribe', {
123
+ method: 'POST',
124
+ accessToken: input.accessToken,
125
+ apiKey: input.apiKey,
126
+ baseUrlMode: 'origin',
127
+ query: { projectId: input.projectId },
128
+ rawBody: buildAsrFormData(input),
129
+ contentType: null,
130
+ });
131
+ }
132
+ transcribeSrt(input) {
133
+ return this.http.request('/api/asr/srt', {
134
+ method: 'POST',
135
+ accessToken: input.accessToken,
136
+ apiKey: input.apiKey,
137
+ baseUrlMode: 'origin',
138
+ query: { projectId: input.projectId },
139
+ rawBody: buildAsrFormData(input),
140
+ contentType: null,
141
+ });
142
+ }
143
+ getAsrTask(input) {
144
+ return this.http.request(`/api/asr/tasks/${input.taskId}`, {
145
+ accessToken: input.accessToken,
146
+ apiKey: input.apiKey,
147
+ baseUrlMode: 'origin',
148
+ query: {
149
+ projectId: input.projectId,
150
+ provider: input.provider,
151
+ },
152
+ });
153
+ }
154
+ async downloadAudio(input) {
155
+ const response = await this.http.requestRaw('/api/files/download', {
156
+ baseUrlMode: 'origin',
157
+ query: { key: input.key },
158
+ });
159
+ if (!response.ok) {
160
+ return throwPlatformError(response);
161
+ }
162
+ return {
163
+ audio: await response.arrayBuffer(),
164
+ contentType: response.headers.get('Content-Type') ?? 'application/octet-stream',
165
+ fileName: parseContentDispositionFileName(response.headers.get('Content-Disposition')),
166
+ headers: response.headers,
167
+ };
168
+ }
169
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api2key-base-sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "TypeScript SDK for consuming the Api2Key API stable APIs",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",