@ai-sdk/gateway 4.0.121 → 4.0.123
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 +30 -0
- package/dist/index.d.ts +137 -42
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +377 -145
- package/dist/index.js.map +1 -1
- package/docs/00-ai-gateway.mdx +93 -11
- package/package.json +3 -3
- package/src/gateway-decision-model-settings.ts +2 -0
- package/src/gateway-language-model-settings.ts +2 -0
- package/src/gateway-provider.ts +71 -7
- package/src/gateway-realtime-auth.ts +21 -3
- package/src/gateway-realtime-model-settings.ts +1 -1
- package/src/gateway-reranking-model-settings.ts +2 -0
- package/src/gateway-speech-model-settings.ts +1 -0
- package/src/gateway-speech-model.ts +305 -1
- package/src/gateway-stream-errors.ts +71 -0
- package/src/gateway-transcription-model-settings.ts +2 -0
- package/src/gateway-transcription-model.ts +18 -72
- package/src/index.ts +2 -0
- package/src/tool/parallel-search.ts +158 -46
package/docs/00-ai-gateway.mdx
CHANGED
|
@@ -464,6 +464,69 @@ console.log(ranking);
|
|
|
464
464
|
|
|
465
465
|
Reranking models are useful for improving search results in retrieval-augmented generation (RAG) pipelines by re-scoring candidate documents after an initial retrieval step.
|
|
466
466
|
|
|
467
|
+
## Streaming Speech Models
|
|
468
|
+
|
|
469
|
+
<Note type="warning">
|
|
470
|
+
Streaming speech is experimental and the API may change in patch releases.
|
|
471
|
+
</Note>
|
|
472
|
+
|
|
473
|
+
Speech models that support streaming can be used with
|
|
474
|
+
`experimental_streamSpeech`. The gateway provider connects to AI Gateway over a
|
|
475
|
+
WebSocket, forwards text deltas as they arrive, and streams audio chunks back.
|
|
476
|
+
Gateway WebSocket auth is carried in the subprotocols, so no header-capable
|
|
477
|
+
WebSocket implementation is needed.
|
|
478
|
+
|
|
479
|
+
Streaming speech works only with models that the
|
|
480
|
+
[AI Gateway model catalog](https://vercel.com/ai-gateway/models) lists with
|
|
481
|
+
streaming speech support. The model ID below is illustrative; check the catalog
|
|
482
|
+
for current availability.
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
import { experimental_streamSpeech as streamSpeech } from 'ai';
|
|
486
|
+
|
|
487
|
+
const result = streamSpeech({
|
|
488
|
+
model: 'elevenlabs/eleven-flash-v2.5',
|
|
489
|
+
text: textStream, // a string, or an array or stream of strings and/or text parts
|
|
490
|
+
outputFormat: { type: 'audio/pcm', rate: 24000 },
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
for await (const chunk of result.audioStream) {
|
|
494
|
+
// Uint8Array audio bytes
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
console.log(await result.usage); // { inputCharacters: ... }
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Streaming Speech in the Browser
|
|
501
|
+
|
|
502
|
+
To stream speech from browser code without exposing your Gateway credential,
|
|
503
|
+
mint a short-lived client secret on your server with
|
|
504
|
+
`gateway.experimental_speech.getToken()` and pass the token to the browser.
|
|
505
|
+
The token is bound to the streaming speech surface and the requested model.
|
|
506
|
+
|
|
507
|
+
```ts
|
|
508
|
+
// Server
|
|
509
|
+
import { gateway } from '@ai-sdk/gateway';
|
|
510
|
+
|
|
511
|
+
const { token } = await gateway.experimental_speech.getToken({
|
|
512
|
+
model: 'elevenlabs/eleven-flash-v2.5',
|
|
513
|
+
expiresAfterSeconds: 60,
|
|
514
|
+
});
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
```ts
|
|
518
|
+
// Browser
|
|
519
|
+
import { createGateway } from '@ai-sdk/gateway';
|
|
520
|
+
import { experimental_streamSpeech as streamSpeech } from 'ai';
|
|
521
|
+
|
|
522
|
+
const gateway = createGateway({ apiKey: token });
|
|
523
|
+
|
|
524
|
+
const result = streamSpeech({
|
|
525
|
+
model: gateway.experimental_speech('elevenlabs/eleven-flash-v2.5'),
|
|
526
|
+
text: textStream,
|
|
527
|
+
});
|
|
528
|
+
```
|
|
529
|
+
|
|
467
530
|
## Realtime Models
|
|
468
531
|
|
|
469
532
|
<Note type="warning">
|
|
@@ -1232,7 +1295,7 @@ for await (const part of result.stream) {
|
|
|
1232
1295
|
|
|
1233
1296
|
#### Parallel Search
|
|
1234
1297
|
|
|
1235
|
-
The Parallel Search tool enables models to search the web using [Parallel AI's Search API](https://docs.parallel.ai/api-reference/search
|
|
1298
|
+
The Parallel Search tool enables models to search the web using [Parallel AI's Search API](https://docs.parallel.ai/api-reference/search/search). The model provides a research objective and a few keyword queries, and the tool returns relevant excerpts from web pages, optimized for LLM consumption.
|
|
1236
1299
|
|
|
1237
1300
|
```ts
|
|
1238
1301
|
import { gateway, generateText } from 'ai';
|
|
@@ -1260,7 +1323,8 @@ const result = await generateText({
|
|
|
1260
1323
|
prompt: 'Find detailed information about TypeScript 5.0 features.',
|
|
1261
1324
|
tools: {
|
|
1262
1325
|
parallel_search: gateway.tools.parallelSearch({
|
|
1263
|
-
mode: '
|
|
1326
|
+
mode: 'advanced',
|
|
1327
|
+
location: 'us',
|
|
1264
1328
|
maxResults: 5,
|
|
1265
1329
|
sourcePolicy: {
|
|
1266
1330
|
includeDomains: ['typescriptlang.org', 'github.com'],
|
|
@@ -1279,11 +1343,15 @@ console.log('Tool results:', JSON.stringify(result.toolResults, null, 2));
|
|
|
1279
1343
|
|
|
1280
1344
|
The Parallel Search tool supports the following optional configuration options:
|
|
1281
1345
|
|
|
1282
|
-
- **mode** _'
|
|
1346
|
+
- **mode** _'turbo' | 'fast' | 'basic' | 'advanced'_
|
|
1347
|
+
|
|
1348
|
+
Search mode. Defaults to `'basic'`. See the [AI Gateway web search docs](https://vercel.com/docs/ai-gateway/models-and-providers/web-search#using-parallel-search) for pricing.
|
|
1349
|
+
- `'turbo'` - Lowest latency, shorter excerpts. Supports English and Japanese only.
|
|
1350
|
+
- `'fast'` - Low latency, shorter excerpts.
|
|
1351
|
+
- `'basic'` - Balanced latency and excerpt depth (default).
|
|
1352
|
+
- `'advanced'` - Most thorough retrieval, higher latency.
|
|
1283
1353
|
|
|
1284
|
-
|
|
1285
|
-
- `'one-shot'` - Comprehensive results with longer excerpts for single-response answers (default)
|
|
1286
|
-
- `'agentic'` - Concise, token-efficient results optimized for multi-step agentic workflows
|
|
1354
|
+
`'one-shot'` and `'agentic'` are deprecated but still accepted: `'one-shot'` runs as `'basic'` and `'agentic'` runs as `'advanced'`.
|
|
1287
1355
|
|
|
1288
1356
|
- **maxResults** _number_
|
|
1289
1357
|
|
|
@@ -1292,20 +1360,34 @@ The Parallel Search tool supports the following optional configuration options:
|
|
|
1292
1360
|
- **sourcePolicy** _object_
|
|
1293
1361
|
|
|
1294
1362
|
Source policy for controlling which domains to include/exclude:
|
|
1295
|
-
- `includeDomains` -
|
|
1296
|
-
- `excludeDomains` -
|
|
1297
|
-
- `afterDate` - Only include results published after this date (
|
|
1363
|
+
- `includeDomains` - Domains or domain/path prefixes to include, such as `'wikipedia.org'` or `'docs.python.org/3'`. When set, `excludeDomains` is ignored.
|
|
1364
|
+
- `excludeDomains` - Domains or domain/path prefixes to exclude
|
|
1365
|
+
- `afterDate` - Only include results published on or after this date (`YYYY-MM-DD`)
|
|
1366
|
+
|
|
1367
|
+
The include and exclude lists can hold up to 200 entries combined.
|
|
1368
|
+
|
|
1369
|
+
- **maxCharsTotal** _number_
|
|
1370
|
+
|
|
1371
|
+
Maximum total characters across all result excerpts.
|
|
1298
1372
|
|
|
1299
1373
|
- **excerpts** _object_
|
|
1300
1374
|
|
|
1301
1375
|
Excerpt configuration for controlling result length:
|
|
1302
1376
|
- `maxCharsPerResult` - Maximum characters per result
|
|
1303
|
-
- `maxCharsTotal` -
|
|
1377
|
+
- `maxCharsTotal` - Deprecated; use the top-level `maxCharsTotal`
|
|
1304
1378
|
|
|
1305
1379
|
- **fetchPolicy** _object_
|
|
1306
1380
|
|
|
1307
1381
|
Fetch policy for controlling content freshness:
|
|
1308
|
-
- `maxAgeSeconds` - Maximum age in seconds
|
|
1382
|
+
- `maxAgeSeconds` - Maximum age of cached content, in seconds, before a live fetch is attempted. Minimum 600; lower values are raised to 600.
|
|
1383
|
+
- `timeoutSeconds` - Timeout for a live fetch, in seconds. The gateway caps it to stay within its own tool timeout.
|
|
1384
|
+
- `disableCacheFallback` - Return an error instead of older cached content when a live fetch fails. Defaults to `false`.
|
|
1385
|
+
|
|
1386
|
+
- **location** _string_
|
|
1387
|
+
|
|
1388
|
+
ISO 3166-1 alpha-2 country code to localize results, such as `'us'`. Invalid codes are ignored with a warning.
|
|
1389
|
+
|
|
1390
|
+
Each tool result contains a `search_id` and `results`, ordered by decreasing relevance, and may include a `session_id`. Each result has a `url`, a `title`, a `publish_date` (both may be `null`), and `excerpts` (an array of markdown strings). The result can also include `warnings`, for example when an invalid `location` is ignored.
|
|
1309
1391
|
|
|
1310
1392
|
The tool works with both `generateText` and `streamText`:
|
|
1311
1393
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/gateway",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "4.0.
|
|
4
|
+
"version": "4.0.123",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@ai-sdk/provider": "4.0.
|
|
34
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
33
|
+
"@ai-sdk/provider": "4.0.33",
|
|
34
|
+
"@ai-sdk/provider-utils": "5.0.67",
|
|
35
35
|
"@vercel/oidc": "3.2.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
@@ -271,6 +271,8 @@ export type GatewayModelId =
|
|
|
271
271
|
| 'sakana/fugu-ultra'
|
|
272
272
|
| 'sakana/fugu-ultra-v2'
|
|
273
273
|
| 'sakana/namazu'
|
|
274
|
+
| 'scaledown/scaledown-extract-1'
|
|
275
|
+
| 'scaledown/scaledown-summarize-1'
|
|
274
276
|
| 'spacexai/grok-4.1-fast-non-reasoning'
|
|
275
277
|
| 'spacexai/grok-4.1-fast-reasoning'
|
|
276
278
|
| 'spacexai/grok-4.20-multi-agent'
|
package/src/gateway-provider.ts
CHANGED
|
@@ -40,7 +40,7 @@ import { GatewayImageModel } from './gateway-image-model';
|
|
|
40
40
|
import { GatewayVideoModel } from './gateway-video-model';
|
|
41
41
|
import { GatewayDecisionModel } from './gateway-decision-model';
|
|
42
42
|
import { GatewayRerankingModel } from './gateway-reranking-model';
|
|
43
|
-
import { GatewaySpeechModel } from './gateway-speech-model';
|
|
43
|
+
import { GatewaySpeechModel, toGatewaySpeechUrl } from './gateway-speech-model';
|
|
44
44
|
import {
|
|
45
45
|
GatewayTranscriptionModel,
|
|
46
46
|
toGatewayTranscriptionUrl,
|
|
@@ -66,6 +66,7 @@ import type {
|
|
|
66
66
|
TranscriptionModelV4,
|
|
67
67
|
Experimental_VideoModelV4,
|
|
68
68
|
Experimental_DecisionModelV4,
|
|
69
|
+
Experimental_StreamingSpeechModelV4,
|
|
69
70
|
Experimental_RealtimeFactoryV4 as RealtimeFactoryV4,
|
|
70
71
|
Experimental_RealtimeFactoryV4GetTokenOptions as RealtimeFactoryV4GetTokenOptions,
|
|
71
72
|
ProviderV4,
|
|
@@ -216,6 +217,14 @@ export interface GatewayProvider extends ProviderV4 {
|
|
|
216
217
|
*/
|
|
217
218
|
experimental_transcription: GatewayTranscriptionFactory;
|
|
218
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Experimental streaming-speech entry point. Callable like
|
|
222
|
+
* `speech(modelId)`, plus `getToken` for minting a short-lived client
|
|
223
|
+
* secret (`vcst_`) a browser can use to open the streaming speech
|
|
224
|
+
* WebSocket without holding the Gateway credential.
|
|
225
|
+
*/
|
|
226
|
+
experimental_speech: GatewaySpeechFactory;
|
|
227
|
+
|
|
219
228
|
/**
|
|
220
229
|
* Gateway-specific tools executed server-side.
|
|
221
230
|
*/
|
|
@@ -252,6 +261,36 @@ export interface GatewayTranscriptionFactory {
|
|
|
252
261
|
): Promise<GatewayTranscriptionFactoryGetTokenResult>;
|
|
253
262
|
}
|
|
254
263
|
|
|
264
|
+
export type GatewaySpeechFactoryGetTokenOptions = {
|
|
265
|
+
model: GatewaySpeechModelId;
|
|
266
|
+
/** Token lifetime in seconds. Gateway default is 60s (max 300s). */
|
|
267
|
+
expiresAfterSeconds?: number;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
export type GatewaySpeechFactoryGetTokenResult = {
|
|
271
|
+
/** The minted `vcst_` client secret. */
|
|
272
|
+
token: string;
|
|
273
|
+
/** WebSocket URL of the streaming speech surface for this model. */
|
|
274
|
+
url: string;
|
|
275
|
+
/** Token expiry, epoch seconds. */
|
|
276
|
+
expiresAt?: number;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Streaming-speech factory: callable like `speech(modelId)`, plus a
|
|
281
|
+
* server-side `getToken` that mints a speech-bound short-lived client secret
|
|
282
|
+
* (`vcst_`). The browser connects with
|
|
283
|
+
* `createGateway({ apiKey: token }).speech(modelId)`; the token rides the
|
|
284
|
+
* same auth subprotocol an API key does, without exposing the credential.
|
|
285
|
+
*/
|
|
286
|
+
export interface GatewaySpeechFactory {
|
|
287
|
+
(modelId: GatewaySpeechModelId): Experimental_StreamingSpeechModelV4;
|
|
288
|
+
|
|
289
|
+
getToken(
|
|
290
|
+
options: GatewaySpeechFactoryGetTokenOptions,
|
|
291
|
+
): Promise<GatewaySpeechFactoryGetTokenResult>;
|
|
292
|
+
}
|
|
293
|
+
|
|
255
294
|
export interface GatewayProviderSettings {
|
|
256
295
|
/** Settings for downloading JSON Lines batch results. */
|
|
257
296
|
batchResultDownloads?: {
|
|
@@ -288,10 +327,10 @@ export interface GatewayProviderSettings {
|
|
|
288
327
|
fetch?: FetchFunction;
|
|
289
328
|
|
|
290
329
|
/**
|
|
291
|
-
* Custom WebSocket implementation used for streaming transcription
|
|
292
|
-
* useful for testing or for runtimes without a
|
|
293
|
-
* header-capable implementation is not required —
|
|
294
|
-
* carried in the subprotocols.
|
|
330
|
+
* Custom WebSocket implementation used for streaming transcription and
|
|
331
|
+
* streaming speech. This is useful for testing or for runtimes without a
|
|
332
|
+
* global WebSocket. A header-capable implementation is not required —
|
|
333
|
+
* Gateway WebSocket auth is carried in the subprotocols.
|
|
295
334
|
*/
|
|
296
335
|
webSocket?: WebSocketConstructor;
|
|
297
336
|
|
|
@@ -391,7 +430,7 @@ export function createGateway(
|
|
|
391
430
|
const mintClientSecret = async (params: {
|
|
392
431
|
modelId: string;
|
|
393
432
|
expiresAfterSeconds?: number;
|
|
394
|
-
routeKind?: 'transcription';
|
|
433
|
+
routeKind?: 'speech' | 'transcription';
|
|
395
434
|
}): Promise<{ token: string; expiresAt?: number }> => {
|
|
396
435
|
assertGatewayClientSecretServerEnvironment();
|
|
397
436
|
const auth = await getRealtimeAuthToken();
|
|
@@ -624,10 +663,35 @@ export function createGateway(
|
|
|
624
663
|
headers: getHeaders,
|
|
625
664
|
fetch: options.fetch,
|
|
626
665
|
o11yHeaders: createO11yHeaders(),
|
|
666
|
+
webSocket: options.webSocket,
|
|
627
667
|
});
|
|
628
668
|
};
|
|
629
669
|
provider.speechModel = createSpeechModel;
|
|
630
670
|
provider.speech = createSpeechModel;
|
|
671
|
+
// Callable like `speech(modelId)`; `getToken` mints a speech-bound client
|
|
672
|
+
// secret (server-side only) the browser uses to connect via
|
|
673
|
+
// `createGateway({ apiKey: token }).speech(modelId)`.
|
|
674
|
+
provider.experimental_speech = Object.assign(
|
|
675
|
+
(modelId: GatewaySpeechModelId) => createSpeechModel(modelId),
|
|
676
|
+
{
|
|
677
|
+
getToken: async (
|
|
678
|
+
tokenOptions: GatewaySpeechFactoryGetTokenOptions,
|
|
679
|
+
): Promise<GatewaySpeechFactoryGetTokenResult> => {
|
|
680
|
+
const secret = await mintClientSecret({
|
|
681
|
+
modelId: tokenOptions.model,
|
|
682
|
+
routeKind: 'speech',
|
|
683
|
+
...(tokenOptions.expiresAfterSeconds != null && {
|
|
684
|
+
expiresAfterSeconds: tokenOptions.expiresAfterSeconds,
|
|
685
|
+
}),
|
|
686
|
+
});
|
|
687
|
+
return {
|
|
688
|
+
token: secret.token,
|
|
689
|
+
url: toGatewaySpeechUrl(baseURL, tokenOptions.model),
|
|
690
|
+
...(secret.expiresAt != null && { expiresAt: secret.expiresAt }),
|
|
691
|
+
};
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
) as GatewaySpeechFactory;
|
|
631
695
|
const createTranscriptionModel = (modelId: GatewayTranscriptionModelId) => {
|
|
632
696
|
return new GatewayTranscriptionModel(modelId, {
|
|
633
697
|
provider: 'gateway',
|
|
@@ -728,7 +792,7 @@ export async function getGatewayAuthToken(
|
|
|
728
792
|
function assertGatewayClientSecretServerEnvironment(): void {
|
|
729
793
|
if (typeof globalThis.window !== 'undefined') {
|
|
730
794
|
throw new Error(
|
|
731
|
-
'AI Gateway client secrets must be minted server-side: minting needs your Gateway credential, which must never reach the browser. Call gateway.experimental_realtime.getToken() or gateway.
|
|
795
|
+
'AI Gateway client secrets must be minted server-side: minting needs your Gateway credential, which must never reach the browser. Call gateway.experimental_realtime.getToken(), gateway.experimental_transcription.getToken(), or gateway.experimental_speech.getToken() from your server and pass the returned token to the client.',
|
|
732
796
|
);
|
|
733
797
|
}
|
|
734
798
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared WebSocket subprotocol contract for AI Gateway realtime
|
|
3
|
-
* transcription auth.
|
|
2
|
+
* Shared WebSocket subprotocol contract for AI Gateway realtime, streaming
|
|
3
|
+
* transcription, and streaming speech auth.
|
|
4
4
|
*
|
|
5
5
|
* The browser `WebSocket` API cannot set request headers, so the Gateway auth
|
|
6
6
|
* (bearer) token is carried through the `Sec-WebSocket-Protocol` handshake
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* This module is the single source of truth for that contract so the client and
|
|
11
11
|
* the Gateway server can't drift: the client encodes values with
|
|
12
|
-
* `getGatewayRealtimeProtocols` / `getGatewayTranscriptionProtocols
|
|
12
|
+
* `getGatewayRealtimeProtocols` / `getGatewayTranscriptionProtocols` /
|
|
13
|
+
* `getGatewaySpeechProtocols`, and the
|
|
13
14
|
* Gateway server decodes them with `getGatewayRealtimeAuthToken` /
|
|
14
15
|
* `getGatewayRealtimeTeamIdOrSlug`.
|
|
15
16
|
*
|
|
@@ -33,6 +34,12 @@ export const GATEWAY_REALTIME_SUBPROTOCOL = 'ai-gateway-realtime.v1';
|
|
|
33
34
|
*/
|
|
34
35
|
export const GATEWAY_TRANSCRIPTION_SUBPROTOCOL = 'ai-gateway-transcription.v1';
|
|
35
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Marker subprotocol for streaming speech handshakes (same negotiation
|
|
39
|
+
* purpose as `GATEWAY_REALTIME_SUBPROTOCOL`).
|
|
40
|
+
*/
|
|
41
|
+
export const GATEWAY_SPEECH_SUBPROTOCOL = 'ai-gateway-speech.v1';
|
|
42
|
+
|
|
36
43
|
/** Subprotocol prefix that carries the Gateway auth (bearer) token. */
|
|
37
44
|
export const GATEWAY_AUTH_SUBPROTOCOL_PREFIX = 'ai-gateway-auth.';
|
|
38
45
|
|
|
@@ -65,6 +72,17 @@ export function getGatewayTranscriptionProtocols(
|
|
|
65
72
|
);
|
|
66
73
|
}
|
|
67
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Client-side: `getGatewayRealtimeProtocols`, but with the streaming speech
|
|
77
|
+
* marker subprotocol.
|
|
78
|
+
*/
|
|
79
|
+
export function getGatewaySpeechProtocols(
|
|
80
|
+
token: string,
|
|
81
|
+
options?: { teamIdOrSlug?: string },
|
|
82
|
+
): string[] {
|
|
83
|
+
return buildGatewayProtocols(GATEWAY_SPEECH_SUBPROTOCOL, token, options);
|
|
84
|
+
}
|
|
85
|
+
|
|
68
86
|
function buildGatewayProtocols(
|
|
69
87
|
marker: string,
|
|
70
88
|
token: string,
|